Etude de cas sur l’analyse et la mesure de performance des Flux de Patients

Introduction

Ce travail porte sur l’analyse des flux de patients sur un plateau mutualisé de consultations externes d’un hôpital. L’objectif est de réaliser un diagnostic objectif de la performance organisationnelle du service d’urologie en s’appuyant sur les méthodes et outils d’analyse de flux vus précédemment ainsi que sur le langage R à travers l’environnement de RStudio. La figure ci-dessous illustre les principaux flux ainsi que le Plan du plateau de consultations (voir fichiers PlanConsultations.pdf et ZoomURO.pdf consultables à partir http://bit.ly/PlansCHUTlse)

Plan du plateau de consultations Afin de collecter des données sur les parcours suivis, les patients qui se sont présentés le 12/11/2015 ont été équipés d’une étiquette électronique (type RFID) qui a permis de tracer leurs parcours dans le plateau de consultation. Les données collectées ont été fusionnées avec les données des outils de gestion des dossiers administratifs et médicaux utilisés par les personnels. L’ensemble est disponible sous la forme d’un fichier log, illustré par le tableau suivant (voir annexe LogPatientUROseul_12112015.xlsx consultable à partir http://bit.ly/logPatients).

Vue du tableau de données de log patient
Vue du tableau de données de log patient

Les différentes de ce tableau de données sont :

  • ID (Col A) : Identifiant du patient
  • Timestamp start (Col B) : horodatage entrée de zone ou salle
  • Timestamp end (Col C) : horodatage sortie de zone ou salle
  • Activity_MACRO (Col D) : Activité suivie et indice salle (i)
  • Activity_DETAILS (Col E) : Type d’activité
  • Ress.Humaines (Col F) : Ressources humaines administratives ou soignantes intervenant dans l’activité
  • distance parcourues (Col G) : Distance parcourue cumulée
  • début/fin opX (Col H à O) : horodatage début/fin de chaque opération de prise en charge par une ressources administrative ou soignante (maxi 4 opérations par activité).

Devoir 2 - Travail demandé : Modélisation data-driven du service d’urologie

Ce travail est à rendre pour le 11/02/2026 (avant le cours) au format Rmd ou R (+pdf si besoin) avec pour titre “NOM_Prenom_FIE5-2-IOS-4-Devoir-2.*”

Ce deuxième devoir se place en suite directe du premier et vous propose un premier type de modélisation data-driven (et top down), c’est-à-dire basé sur les travaux de Whitt & Zhang (2017) A data-driven model of an emergency department.

Question 1) Loi de Little (/3)

Pour rappel, la loi de Little est une loi fondamentale qui, dans un cadre asymptotique, lie le niveau d’occupation moyen au temps d’attente moyen par le taux d’arrivée moyen selon la formule donnée ci-dessous : \[ L = \lambda W \] - \(L\) : Niveau d’occupation moyen (asymptotique) - \(\lambda\) : Taux d’arrivée moyen (asymptotique) - \(W\) : Temps d’attente moyen (asymptotique)

Cette loi s’applique quel que soit le système ou modèle considéré à partir du moment où \(L<+\infty\), \(W<+\infty\) et \(\lambda<+\infty\) (Little JDC, Graves SC. Little’s law. In: International series in operations research and management science. 2008: 81–100.)

Pour cette première question, il vous est demandé de vérifier que cette loi “s’applique” bien sur notre service d’urologie durant la période de 8h à 18h de la journée du 12/11/2015.

Réponse :

Attention : utiliser bien les **arrivées initiales** des patients et pas les arrivées intermédiaires après une transition
  • Calcul le niveau d’occupation moyen :

    Rappel et conseil : \(L = \frac{1}{\int dt} \int l(t)dt\) avec \(l(t) = A(t) - D(t)\), vous pouvez calculer en décomposant la période en zone \(i\) de même niveau d’occupation et en calculant \(L[8h;18h] = \frac{1}{\sum_i t_i} \sum_i l_i t_i\) avec \(t_i\) la durée de la zone \(i\)

library(readxl)
library(dplyr)

# Calcul de L
# Load the Excel dataset Patient URO
df <- read_excel("Log_Patient_URO_12112015.xlsx")

# Rename columns for easier handling
df1 <- df %>%
  rename(
    Ress_Humaines = `Ress. Humaines`,
    Timestamp_start = `Timestamp start`,
    Timestamp_end   = `Timestamp end`,
    DISTANCE_PARCOURUE = `distance parcourue`
  ) %>%
  mutate(
    Timestamp_start = as.POSIXct(Timestamp_start,
                                 format = "%d/%m/%Y %H:%M:%S",
                                 tz = "Europe/Paris")
  )

# Get time in system based on entry and exit
temps_systeme <- df1 %>%
  group_by(ID) %>%
  summarise(
    entree = min(Timestamp_start, na.rm = TRUE),
    sortie = max(Timestamp_start,   na.rm = TRUE)
  ) %>%
  ungroup()

events_arr <- temps_systeme %>%
  dplyr::select(time = entree) %>%
  mutate(delta = 1)

events_dep <- temps_systeme %>%
  dplyr::select(time = sortie) %>%
  mutate(delta = -1)

events <- bind_rows(events_arr, events_dep) %>%
  arrange(time)

events <- events %>%
  mutate(
    L_t = cumsum(delta),
    dt = as.numeric(difftime(lead(time), time, units = "hours"))
  )

L <- sum(events$L_t * events$dt, na.rm = TRUE) /
     sum(events$dt, na.rm = TRUE)

L
[1] 9.134184
  • Calcul du taux d’arrivée moyen :
library(dplyr)
library(readxl)
library(tidyverse)
# Calcul de lamba
# Load the Excel dataset Patient URO
df <- read_excel("Log_Patient_URO_12112015.xlsx")
df

# Rename columns for easier handling
df1 <- df %>%
   rename(Ress_Humaines = `Ress. Humaines`,
        Timestamp_start = `Timestamp start`,
        Timestamp_end   = `Timestamp end`,
        DISTANCE_PARCOURUE = `distance parcourue`)

arrivees <- df1 %>%
  filter(Activity_MACRO == "Entrée des Consultations") %>%
  group_by(ID) %>%
  summarise(arrivee = min(Timestamp_start)) %>%
  ungroup()

arrivees

arrivees_8_18 <- arrivees %>%
  filter(format(arrivee, "%H:%M:%S") >= "08:00:00",
         format(arrivee, "%H:%M:%S") <= "18:00:00")

arrivees_8_18

lambda <- nrow(arrivees_8_18) / 10
lambda
[1] 6.8
  • Calcul du niveau d’attente moyen :
# Load the Excel dataset Patient URO
df <- read_excel("Log_Patient_URO_12112015.xlsx")
df

# Rename columns for easier handling
df1 <- df %>%
   rename(Ress_Humaines = `Ress. Humaines`,
        Timestamp_start = `Timestamp start`,
        Timestamp_end   = `Timestamp end`,
        DISTANCE_PARCOURUE = `distance parcourue`)

# Get time in system based on entry and exit
temps_systeme <- df1 %>%
  group_by(ID) %>%
  summarise(
    entree = min(Timestamp_start, na.rm = TRUE),
    sortie = max(Timestamp_start, na.rm = TRUE)
  ) %>%
  ungroup()

temps_systeme

temps_systeme <- temps_systeme %>%
  mutate(W = as.numeric(difftime(sortie, entree, units = "hours")))

W <- mean(temps_systeme$W, na.rm = TRUE)
W
[1] 1.772818
  • Comparaison de \(L\) et \(\lambda W\) :
```r
library(dplyr)
library(readxl)

# OCCUPATION
events <- bind_rows(
  temps_systeme %>% transmute(time = entree, delta = 1),
  temps_systeme %>% transmute(time = sortie, delta = -1)
) %>%
  arrange(time) %>%
  mutate(
    L_t = cumsum(delta),
    dt = as.numeric(difftime(lead(time), time, units = "hours"))
  )

L <- sum(events$L_t * events$dt, na.rm = TRUE) /
     sum(events$dt, na.rm = TRUE)

L 
```
```
[1] 9.134184
```
```r
lambda*W
```
```
[1] 12.05516
```
  • Rappel et conseil : Tenter d’expliquer pourquoi on observe une différence. En particulier, pour cette explication penser à pourquoi \(\lambda W\) peut être assimilée à une moyenne des niveaux de présence moyen de patients \(m\) : \(\frac{1}{M}\sum_{m \in M} \frac{1}{t}\int_0^t \mathbb{1}_m(t) dt\) avec \(\mathbb{1}_m(t)\) la fonction d’indicatrice de présence du patient \(m\)

Réponse :

On observe que L≈9 alors que λ*W≈12 La différence s’explique par le fait que :

  • Effets de bord temporels : La journée ne couvre pas un cycle complet (patients arrivés avant 8h ou partis après 18h)

  • Non-stationnarité : Les arrivées sont concentrées le matin, les départs étalés

  • Moyenne temporelle vs moyenne par patient : L est une moyenne dans le temps, λW est une moyenne sur les patients

Question 2) Processus de Poisson d’arrivée non homogène (/3)

Un processus de poisson est un processus de comptage (dans le temps) indiquant un nombre évènements ayant occurés entre un temps \(0\) et un temps \(t\) selon une distribution de Poisson \(\mathcal(P)(\lambda * t)\) avec un taux par unité de temps \(\lambda\). Nous verrons dans le cours 3 que les temps entre chaque événement suive une loi de distribution exponentielle de paramètre \(\lambda\).

Un processus de Poisson non homogène (NHPP) est un processus de comptage où le taux d’événement \(\lambda(t)\) n’est pas constant. En considérant une modélisation de NHPP basé sur taux d’arrivées des tranches [8h;10h], ]10h;12h], …, ]16h;18h] du service d’urologie générer 30 échantillons de ce NHPP sur la période 8h, 18h et illustrés les.

Pour vous aider voici un exemple pour un Processus de Poisson homogène : (n’hésitez pas à aller plus loin aussi pour l’illustration)

lambda = 10 # par heure
samples <- tibble(run=to_vec(for(i in 1:10) rep(i,200)),
       id=rep(1:200,10),
       delta_t=rexp(2000,lambda)) %>%
  group_by(run) %>%
  mutate(t = cumsum(delta_t)) %>%
  filter(t <= 10)

# illustration 1
samples %>% 
  ggplot(aes(t,id,color=factor(run))) +
  geom_point() 


# illustration 2
samples %>%   
  mutate(t = cut(t,seq(0,30,by=1),include.lowest = TRUE)) %>%
  count(run,t) %>%
  arrange(run,t) %>%
  ggplot(aes(t,n)) +
  geom_boxplot()

Attention : pour un processus de Poisson non homogène, il faudra générer le nombre d'arrivées avec _rpoiss_ pour chaque tranche de temps avec un taux différent et ensuite les répartir uniformément dans le temps (au sein de leur intervalle) en générant des valuers uniform (_runif_).

Réponse :


# Import des données
data_raw <- read_excel("Log_Patient_URO_12112015.xlsx")

# on garde que l'arrivée initiale
data_arrivals <- data_raw %>%
  mutate(arrival_time = ymd_hms(`Timestamp start`)) %>%
  group_by(ID) %>%                
  slice_min(arrival_time, n = 1) %>%        
  ungroup() %>%
  mutate(
    hour = hour(arrival_time) + minute(arrival_time)/60
  ) %>%
  filter(hour >= 8, hour < 18)

# Découpage en tranches horaires
data_arrivals <- data_arrivals %>%
  mutate(
    tranche = cut(
      hour,
      breaks = c(8,10,12,14,16,18),
      include.lowest = TRUE,
      right = TRUE
    )
  )

# Estimation des taux λ(t)
lambda_hat <- data_arrivals %>%
  count(tranche) %>%
  mutate(lambda = n / 2) 

# Définition des intervalles
intervals <- tibble(
  start  = c(8,10,12,14,16),
  end    = c(10,12,14,16,18),
  lambda = lambda_hat$lambda
)

# Simulation du NHPP (30 runs)
set.seed(123)
n_runs <- 30

samples_nhpp <- map_dfr(1:n_runs, function(run_id) {

  map_dfr(1:nrow(intervals), function(i) {

    dt <- intervals$end[i] - intervals$start[i]
    n_events <- rpois(1, intervals$lambda[i] * dt)

    tibble(
      run = run_id,
      t = runif(n_events, intervals$start[i], intervals$end[i])
    )
  })
}) %>%
  arrange(run, t) %>%
  group_by(run) %>%
  mutate(id = row_number()) %>%
  ungroup()

# Illustration 1 : trajectoires
samples_nhpp %>%
  ggplot(aes(x = t, color = factor(run))) +
  stat_ecdf(geom = "step", alpha = 0.5) +
  scale_y_continuous(labels = scales::percent) +
  labs(
    title = "Trajectoires du NHPP – Fonction de comptage",
    x = "Temps (heures)",
    y = "Proportion d'arrivées cumulées"
  ) +
  theme_minimal() +
  theme(legend.position = "none")


# Calcul des arrivées réelles par heure
real_arrivals <- data_arrivals %>%
  mutate(hour = cut(hour, seq(8,18,by=1), include.lowest = TRUE)) %>%
  count(hour)

# Illustration 2 AMÉLIORÉE
samples_nhpp %>%
  mutate(hour = cut(t, seq(8,18,by=1), include.lowest = TRUE)) %>%
  count(run, hour) %>%
  ggplot(aes(hour, n)) +
  geom_boxplot(fill = "lightblue", alpha = 0.6) +
  geom_point(data = real_arrivals, aes(hour, n), 
             color = "red", size = 3, shape = 18) +  
  labs(
    title = "Distribution des arrivées par heure",
    subtitle = "Boxplot: NHPP simulé (30 runs) | Losanges rouges: données réelles",
    x = "Heure",
    y = "Nombre d'arrivées"
  ) +
  theme_minimal()

Interprétation des résultats :

Ce graphique représente plusieurs trajectoires simulées de la fonction de comptage cumulée d’un processus de Poisson non homogène (NHPP) au cours du temps, exprimé en heures. Chaque courbe colorée correspond à une réalisation possible des arrivées, ce qui explique les écarts observés entre elles et traduit le caractère aléatoire du processus. Toutes les trajectoires sont croissantes, passant progressivement de 0 % à 100 % d’arrivées, puisque les événements s’accumulent sans jamais diminuer. La pente des courbes varie selon les périodes : elle est plus faible au début de l’intervalle temporel, s’accentue nettement au milieu, indiquant une intensité d’arrivée plus élevée, puis ralentit en fin de période lorsque la totalité des arrivées est presque atteinte. L’ensemble met ainsi en évidence une intensité dépendante du temps, caractéristique principale d’un processus de Poisson non homogène, tout en montrant la variabilité naturelle entre différentes réalisations autour d’une tendance moyenne commune.

Le second graphique montre la distribution du nombre d’arrivées par tranche horaire, en comparant un modèle simulé NHPP aux données réelles. Chaque boxplot résume la variabilité des arrivées simulées pour une heure donnée (médiane, dispersion, valeurs extrêmes), tandis que le losange indique la valeur observée. On voit une augmentation progressive des arrivées en matinée entre 8h et 12h, un creux marqué autour de 12h–14h, puis une reprise nette en milieu d’après-midi (14h–16h), avant une diminution en fin de journée. Globalement, les losanges rouges se situent souvent à l’intérieur ou proches des boxplots, ce qui suggère que le modèle reproduit correctement le niveau et la variabilité des arrivées selon l’heure, même si quelques écarts apparaissent, notamment sur la tranche horaire de 10h-11h où la donné réelle est bien plus haute que celles simulées.

En conclusion, l’analyse conjointe des trajectoires cumulées et des distributions horaires montre que le processus de Poisson non homogène constitue une modélisation globalement pertinente des arrivées observées. Le NHPP capture correctement la dépendance temporelle de l’intensité, en reproduisant à la fois la dynamique globale des arrivées et les variations marquées selon les tranches horaires, notamment le creux de la mi-journée et le pic de l’après-midi. La proximité des données réelles avec les distributions simulées confirme la capacité du modèle à rendre compte de la variabilité naturelle du phénomène. Néanmoins, certains écarts, comme la sous-estimation des arrivées sur la tranche 10h–11h, suggèrent que l’intensité pourrait être affinée localement afin d’améliorer l’adéquation du modèle.

Question 3) Modélisation de la durée de séjour par une distribution (/3)

A l’aide de la documentation “Ricci-distributions-en.pdf” fournie, notamment avec la fonction fitdistr() de la librairie MASS, tester la modélisation (par MLE, Maximum Likelihood Estimation) la durée de séjours de l’ensemble des patients avec différentes distributions : normale, exponentielle, gamma, weibull.

Réponse :

Entrez votre texte ici

#_Entrez votre code R ici_

library(readxl)
library(MASS)

data_service <- read_excel("Log_Patient_URO_12112015.xlsx")

data_raw <- data_service %>%
  mutate(
    datetime_begin = ymd_hms(`Timestamp start`),
    datetime_end   = ymd_hms(`Timestamp end`)
  )

data_patient <- data_raw %>%
  group_by(ID) %>%
  summarise(
    arrival_time = min(datetime_begin, na.rm = TRUE),
    departure_time = max(datetime_end, na.rm = TRUE),
    .groups = "drop"
  )

data_patient <- data_patient %>%
  mutate(
    duree_sejour = as.numeric(difftime(departure_time,
                                       arrival_time,
                                       units = "hours"))
  )

duree <- data_patient$duree_sejour
duree <- duree[!is.na(duree)]
duree <- duree[duree > 0]

summary(duree)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
 0.4654  0.7964  1.5256  1.7322  2.4347  5.8699 
hist(duree, breaks = 30)


fit_norm <- fitdistr(duree, "normal")
fit_norm
      mean          sd    
  1.73217947   1.12042754 
 (0.13587180) (0.09607587)
fit_exp <- fitdistr(duree, "exponential")
fit_exp
     rate   
  0.5773074 
 (0.0700088)
fit_gamma <- fitdistr(duree, "gamma")
fit_gamma
     shape       rate   
  2.6808643   1.5476811 
 (0.4342089) (0.2756369)
fit_weib <- fitdistr(duree, "weibull")
fit_weib
     shape       scale  
  1.6654502   1.9525277 
 (0.1507457) (0.1506968)
AIC_fitdistr <- function(fit) {
  k <- length(fit$estimate)
  -2 * fit$loglik + 2 * k
}

AIC_norm  <- AIC_fitdistr(fit_norm)
AIC_exp   <- AIC_fitdistr(fit_exp)
AIC_gamma <- AIC_fitdistr(fit_gamma)
AIC_weib  <- AIC_fitdistr(fit_weib)

AIC_values <- data.frame(
  Distribution = c("Normale", "Exponentielle", "Gamma", "Weibull"),
  AIC = c(AIC_norm, AIC_exp, AIC_gamma, AIC_weib)
)

AIC_values[order(AIC_values$AIC), ]

hist(duree, prob = TRUE, breaks = 30,
     main = "Ajustement des lois – Durée de séjour",
     xlab = "Durée")

curve(dnorm(x,
            mean = fit_norm$estimate[1],
            sd   = fit_norm$estimate[2]),
      add = TRUE, col = "blue", lwd = 2)

curve(dexp(x,
           rate = fit_exp$estimate),
      add = TRUE, col = "red", lwd = 2)

curve(dgamma(x,
             shape = fit_gamma$estimate["shape"],
             rate  = fit_gamma$estimate["rate"]),
      add = TRUE, col = "green", lwd = 2)

curve(dweibull(x,
               shape = fit_weib$estimate["shape"],
               scale = fit_weib$estimate["scale"]),
      add = TRUE, col = "purple", lwd = 2)

legend("topright",
       legend = c("Normale", "Exponentielle", "Gamma", "Weibull"),
       col = c("blue", "red", "green", "purple"),
       lwd = 2)

Quelles est la meilleure distribution ? (Justifiez) Comparez aussi les moyennes, écart-types et coefficients de variation obtenus pour chaque distribution et par rapport au calcul direct des indicateurs sur les variables statistiques.

Réponse :

Parmi les distributions étudiées, la loi de Weibull apparaît comme la plus appropriée pour modéliser la durée de séjour. Elle respecte le support strictement positif de la variable et reproduit correctement les principaux indicateurs statistiques, en particulier la variabilité observée. La loi Gamma aurait également pu constituer un choix pertinent, car elle présente des caractéristiques proches et un bon accord avec les données empiriques ; toutefois, la Weibull offre une flexibilité légèrement supérieure, notamment dans la modélisation des durées extrêmes et dans l’interprétation du comportement du taux de sortie au cours du séjour. La loi normale, bien que numériquement proche, reste conceptuellement inadaptée en raison de son support non borné inférieur, tandis que la loi exponentielle est clairement inappropriée car elle impose une variabilité trop élevée par rapport aux observations. Ainsi, la loi de Weibull est retenue comme meilleur compromis, la loi Gamma pouvant être considérée comme une alternative valable en second choix.

#_Entrez votre code R ici_

# Indicateurs empiriques
mean_emp <- mean(duree)
sd_emp   <- sd(duree)
cv_emp   <- sd_emp / mean_emp

mean_norm <- fit_norm$estimate["mean"]
sd_norm   <- fit_norm$estimate["sd"]
cv_norm   <- sd_norm / mean_norm

rate_exp <- fit_exp$estimate["rate"]

mean_exp <- 1 / rate_exp
sd_exp   <- 1 / rate_exp
cv_exp   <- sd_exp / mean_exp

shape_g <- fit_gamma$estimate["shape"]
rate_g  <- fit_gamma$estimate["rate"]

mean_gamma <- shape_g / rate_g
sd_gamma   <- sqrt(shape_g) / rate_g
cv_gamma   <- sd_gamma / mean_gamma

shape_w <- fit_weib$estimate["shape"]
scale_w <- fit_weib$estimate["scale"]

mean_weib <- scale_w * gamma(1 + 1 / shape_w)
sd_weib   <- scale_w * sqrt(
  gamma(1 + 2 / shape_w) - gamma(1 + 1 / shape_w)^2
)
cv_weib <- sd_weib / mean_weib

comparaison_table <- data.frame(
  Distribution = c("Empirique", "Normale", "Exponentielle", "Gamma", "Weibull"),
  Moyenne = c(mean_emp, mean_norm, mean_exp, mean_gamma, mean_weib),
  Ecart_type = c(sd_emp, sd_norm, sd_exp, sd_gamma, sd_weib),
  Coefficient_variation = c(cv_emp, cv_norm, cv_exp, cv_gamma, cv_weib)
)

comparaison_table
NA

Quel que soit votre réponse, refaites ce travail pour la distribution gamma en séparant la modélisation des durées de séjour des patients prioritaires et non prioritaires et comparez les deux distributions graphiquement et à l’aide leur moments (espérance, variance, coefficient de variation) et/ou leur paramètres d’échelle et de forme.

Réponse :

L’analyse des durées de séjour des patients selon une loi Gamma ci-dessous montre que, bien que la durée moyenne soit très proche entre les patients prioritaires (≈1,76) et non prioritaires (≈1,72), la dispersion diffère sensiblement. Les patients prioritaires présentent une variance plus élevée (1,66 contre 0,91) et un coefficient de variation plus important (0,73 contre 0,55), indiquant une plus grande variabilité relative de leurs durées de séjour. Cette différence se reflète également dans les paramètres de la loi Gamma : le paramètre shape des patients prioritaires (≈1,86) est plus faible que celui des non prioritaires (≈3,26), ce qui traduit une distribution plus asymétrique et étalée, avec une queue plus longue à droite. À l’inverse, la distribution des non prioritaires est plus concentrée autour de la moyenne. Ainsi, même si les durées moyennes sont similaires, les patients prioritaires montrent des séjours plus hétérogènes, ce qui peut avoir des implications pour la planification hospitalière et la gestion des ressources.

#_Entrez votre code R ici_

data_patient <- data_raw %>%
  mutate(
    datetime_begin = ymd_hms(`Timestamp start`),
    datetime_end   = ymd_hms(`Timestamp end`)
  ) %>%
  group_by(ID) %>%
  summarise(
    arrival_time = min(datetime_begin, na.rm = TRUE),
    departure_time = max(datetime_end, na.rm = TRUE),
    prioritaire = ifelse(any(grepl("PRIO", Activity_DETAILS)), "Oui", "Non"),
    .groups = "drop"
  ) %>%
  mutate(
    duree_sejour = as.numeric(difftime(departure_time,
                                       arrival_time,
                                       units = "hours"))
  )

duree_prio <- data_patient %>%
  filter(prioritaire == "Oui") %>%
  pull(duree_sejour)

duree_non_prio <- data_patient %>%
  filter(prioritaire == "Non") %>%
  pull(duree_sejour)

# Nettoyage
duree_prio <- duree_prio[duree_prio > 0 & !is.na(duree_prio)]
duree_non_prio <- duree_non_prio[duree_non_prio > 0 & !is.na(duree_non_prio)]

library(MASS)

fit_gamma_prio <- fitdistr(duree_prio, "gamma")
fit_gamma_non_prio <- fitdistr(duree_non_prio, "gamma")

params_gamma <- data.frame(
  Groupe = c("Prioritaires", "Non prioritaires"),
  Shape = c(fit_gamma_prio$estimate["shape"],
            fit_gamma_non_prio$estimate["shape"]),
  Rate = c(fit_gamma_prio$estimate["rate"],
           fit_gamma_non_prio$estimate["rate"])
)

params_gamma

moments_gamma <- data.frame(
  Groupe = c("Prioritaires", "Non prioritaires"),
  Esperance = c(
    fit_gamma_prio$estimate["shape"] / fit_gamma_prio$estimate["rate"],
    fit_gamma_non_prio$estimate["shape"] / fit_gamma_non_prio$estimate["rate"]
  ),
  Variance = c(
    fit_gamma_prio$estimate["shape"] / fit_gamma_prio$estimate["rate"]^2,
    fit_gamma_non_prio$estimate["shape"] / fit_gamma_non_prio$estimate["rate"]^2
  ),
  Coefficient_variation = c(
    1 / sqrt(fit_gamma_prio$estimate["shape"]),
    1 / sqrt(fit_gamma_non_prio$estimate["shape"])
  )
)

moments_gamma

hist(duree_prio, prob = TRUE, breaks = 30,
     col = rgb(1,0,0,0.35),
     xlim = range(c(duree_prio, duree_non_prio)),
     main = "Comparaison des durées de séjour – Loi Gamma",
     xlab = "Durée (heures)")

curve(dgamma(x,
             shape = fit_gamma_prio$estimate["shape"],
             rate  = fit_gamma_prio$estimate["rate"]),
      col = "red", lwd = 2, add = TRUE)

hist(duree_non_prio, prob = TRUE, breaks = 30,
     col = rgb(0,0,1,0.35),
     add = TRUE)

curve(dgamma(x,
             shape = fit_gamma_non_prio$estimate["shape"],
             rate  = fit_gamma_non_prio$estimate["rate"]),
      col = "blue", lwd = 2, add = TRUE)

legend("topright",
       legend = c("Prioritaires", "Non prioritaires"),
       col = c("red", "blue"),
       lwd = 2)

# Q-Q plot pour prioritaires
par(mfrow = c(1, 2))


# Prioritaires
qqplot(qgamma(ppoints(length(duree_prio)),
              shape = fit_gamma_prio$estimate["shape"],
              rate = fit_gamma_prio$estimate["rate"]),
       duree_prio,
       main = "Q-Q Plot - Prioritaires",
       xlab = "Quantiles théoriques (Gamma)",
       ylab = "Quantiles observés")
abline(0, 1, col = "red")

# Non-prioritaires
qqplot(qgamma(ppoints(length(duree_non_prio)),
              shape = fit_gamma_non_prio$estimate["shape"],
              rate = fit_gamma_non_prio$estimate["rate"]),
       duree_non_prio,
       main = "Q-Q Plot - Non prioritaires",
       xlab = "Quantiles théoriques (Gamma)",
       ylab = "Quantiles observés")
abline(0, 1, col = "blue")

par(mfrow = c(1, 1))

Question 4) Modélisation de la durée de séjour par régression linéaire (/3)

A l’aide de la fonction lm, construisez et analysez un modèle de régression linéaire estimant la durée de séjour qui prennent en variable d’entrée le bloc de 2h [8h;10h], …, ]16h;18h].

Voici un petit exemple d’utilisation :

Y = c(1, 1.5, 2, 3)
X = c("A","A","B","B")

model <- lm(Y ~ X)
summary(model) 

Call:
lm(formula = Y ~ X)

Residuals:
    1     2     3     4 
-0.25  0.25 -0.50  0.50 

Coefficients:
            Estimate Std. Error t value Pr(>|t|)  
(Intercept)   1.2500     0.3953   3.162   0.0871 .
XB            1.2500     0.5590   2.236   0.1548  
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 0.559 on 2 degrees of freedom
Multiple R-squared:  0.7143,    Adjusted R-squared:  0.5714 
F-statistic:     5 on 1 and 2 DF,  p-value: 0.1548
predict(model) # predict(model,newdata=[new_dataframe]) if new data
   1    2    3    4 
1.25 1.25 2.50 2.50 

Réponse :

Entrez votre texte ici


library(dplyr)
library(lubridate)
library(ggplot2)
library(readxl)
library(broom)  


# Chargement des données
df <- read_excel("Log_Patient_URO_12112015.xlsx")

# Renommer les colonnes
df1 <- df %>%
  rename(
    Ress_Humaines = `Ress. Humaines`,
    Timestamp_start = `Timestamp start`,
    Timestamp_end   = `Timestamp end`,
    DISTANCE_PARCOURUE = `distance parcourue`
  ) %>%
  mutate(
    Timestamp_start = as.POSIXct(Timestamp_start, format = "%d/%m/%Y %H:%M:%S", tz = "Europe/Paris"),
    Timestamp_end   = as.POSIXct(Timestamp_end, format = "%d/%m/%Y %H:%M:%S", tz = "Europe/Paris")
  )

# Calculer la durée de séjour et l'heure d'arrivée pour chaque patient
temps_systeme <- df1 %>%
  group_by(ID) %>%
  summarise(
    entree = min(Timestamp_start, na.rm = TRUE),
    sortie = max(Timestamp_end, na.rm = TRUE),
    .groups = "drop"
  ) %>%
  mutate(
    W = as.numeric(difftime(sortie, entree, units = "hours")),
    heure_entree = hour(entree) + minute(entree)/60
  )

# Créer la variable bloc_2h selon l'heure d'arrivée
temps_systeme <- temps_systeme %>%
  mutate(
    bloc_2h = case_when(
      heure_entree >= 8 & heure_entree < 10 ~ "[08h;10h]",
      heure_entree >= 10 & heure_entree < 12 ~ "[10h;12h]",
      heure_entree >= 12 & heure_entree < 14 ~ "[12h;14h]",
      heure_entree >= 14 & heure_entree < 16 ~ "[14h;16h]",
      heure_entree >= 16 & heure_entree < 18 ~ "[16h;18h]",
      TRUE ~ NA_character_
    )
  ) %>%
  filter(!is.na(bloc_2h), W > 0, !is.na(W))

temps_systeme <- temps_systeme %>%
  mutate(
    bloc_2h = factor(bloc_2h, 
                     levels = c("[08h;10h]", "[10h;12h]", "[12h;14h]", 
                                "[14h;16h]", "[16h;18h]"))
  )

# Vérification
head(temps_systeme)
summary(temps_systeme$W)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
 0.4654  0.7963  1.5256  1.7189  2.4347  5.8699 
model1 <- lm(W ~ bloc_2h, data = temps_systeme)
summary(model1)

Call:
lm(formula = W ~ bloc_2h, data = temps_systeme)

Residuals:
    Min      1Q  Median      3Q     Max 
-1.5650 -0.8471 -0.1567  0.7813  3.7957 

Coefficients:
                 Estimate Std. Error t value Pr(>|t|)  
(Intercept)        0.9368     0.4557   2.056   0.0443 *
bloc_2h[10h;12h]   0.6305     0.5196   1.213   0.2299  
bloc_2h[12h;14h]   0.7602     0.5765   1.319   0.1923  
bloc_2h[14h;16h]   1.1374     0.5582   2.038   0.0461 *
bloc_2h[16h;18h]   1.0119     0.5344   1.894   0.0632 .
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 1.116 on 59 degrees of freedom
Multiple R-squared:  0.08116,   Adjusted R-squared:  0.01887 
F-statistic: 1.303 on 4 and 59 DF,  p-value: 0.2795
# Prédictions
temps_systeme$pred_W1 <- predict(model1)

# Visualisation
ggplot(temps_systeme, aes(x = bloc_2h, y = W)) +
  geom_jitter(width = 0.2, height = 0, alpha = 0.5, color = "lightblue", size = 2) +
  geom_point(aes(y = pred_W1), color = "red", size = 4, shape = 17) +
  stat_summary(fun = mean, geom = "point", color = "darkgreen", size = 4, shape = 18) +
  labs(
    title = "Modèle 1 : Durée de séjour selon le bloc horaire",
    subtitle = "Triangles rouges = prédictions | Losanges verts = moyennes observées",
    x = "Bloc horaire d'arrivée",
    y = "Durée de séjour W (heures)"
  ) +
  theme_minimal() +
  theme(
    plot.title = element_text(face = "bold", size = 14),
    axis.text.x = element_text(angle = 0)
  )


plot(model1, which = 1, main = "Modèle 1 - Résidus vs Valeurs ajustées")

plot(model1, which = 2, main = "Modèle 1 - Q-Q plot")

plot(model1, which = 3, main = "Modèle 1 - Scale-Location")

plot(model1, which = 5, main = "Modèle 1 - Résidus vs Levier")


# Identifier les patients prioritaires
temps_systeme2 <- temps_systeme %>%
  left_join(
    df1 %>%
      group_by(ID) %>%
      summarise(prioritaire = ifelse(any(grepl("PRIO", Activity_DETAILS)), "Oui", "Non"),
                .groups = "drop"),
    by = "ID"
  )

temps_systeme2 <- temps_systeme2 %>%
  mutate(prioritaire = factor(prioritaire, levels = c("Non", "Oui")))

# Vérification
table(temps_systeme2$prioritaire)

Non Oui 
 45  19 
# Modèle 2
model2 <- lm(W ~ bloc_2h + prioritaire, data = temps_systeme2)
summary(model2)

Call:
lm(formula = W ~ bloc_2h + prioritaire, data = temps_systeme2)

Residuals:
    Min      1Q  Median      3Q     Max 
-1.5567 -0.8390 -0.1515  0.7885  3.7790 

Coefficients:
                 Estimate Std. Error t value Pr(>|t|)  
(Intercept)       0.93268    0.46253   2.016   0.0484 *
bloc_2h[10h;12h]  0.62588    0.52714   1.187   0.2399  
bloc_2h[12h;14h]  0.75690    0.58286   1.299   0.1992  
bloc_2h[14h;16h]  1.13320    0.56530   2.005   0.0497 *
bloc_2h[16h;18h]  1.00986    0.53958   1.872   0.0663 .
prioritaireOui    0.02496    0.31047   0.080   0.9362  
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 1.126 on 58 degrees of freedom
Multiple R-squared:  0.08126,   Adjusted R-squared:  0.002062 
F-statistic: 1.026 on 5 and 58 DF,  p-value: 0.4109
# Prédictions
temps_systeme2$pred_W2 <- predict(model2)

# Visualisation
ggplot(temps_systeme2, aes(x = bloc_2h, y = W, color = prioritaire)) +
  geom_jitter(width = 0.2, height = 0, alpha = 0.4, size = 2) +
  geom_point(aes(y = pred_W2), shape = 17, size = 4) +
  geom_line(aes(y = pred_W2, group = prioritaire), linewidth = 1.2) +
  labs(
    title = "Modèle 2 : W ~ bloc_2h + prioritaire",
    subtitle = "Triangles = prédictions | Lignes parallèles montrent l'effet additif",
    x = "Bloc horaire d'arrivée",
    y = "Durée de séjour W (heures)",
    color = "Prioritaire"
  ) +
  scale_color_manual(values = c("Non" = "steelblue", "Oui" = "tomato")) +
  theme_minimal() +
  theme(
    plot.title = element_text(face = "bold", size = 14),
    legend.position = "top"
  )


# Diagnostics UN PAR UN
plot(model2, which = 1, main = "Modèle 2 - Résidus vs Valeurs ajustées")

plot(model2, which = 2, main = "Modèle 2 - Q-Q plot")

plot(model2, which = 3, main = "Modèle 2 - Scale-Location")

plot(model2, which = 5, main = "Modèle 2 - Résidus vs Levier")


models_comparison <- tibble(
  Modèle = c("Modèle 1: W ~ bloc_2h", 
             "Modèle 2: W ~ bloc_2h + prioritaire"),
  R_squared = c(summary(model1)$r.squared, 
                summary(model2)$r.squared),
  R_squared_adj = c(summary(model1)$adj.r.squared, 
                    summary(model2)$adj.r.squared),
  AIC = c(AIC(model1), AIC(model2)),
  BIC = c(BIC(model1), BIC(model2))
)

print(models_comparison)
# A tibble: 2 × 5
  Modèle                              R_squared R_squared_adj   AIC   BIC
  <chr>                                   <dbl>         <dbl> <dbl> <dbl>
1 Modèle 1: W ~ bloc_2h                  0.0812       0.0189   203.  215.
2 Modèle 2: W ~ bloc_2h + prioritaire    0.0813       0.00206  204.  220.
# Test de comparaison (ANOVA)
anova(model1, model2)
Analysis of Variance Table

Model 1: W ~ bloc_2h
Model 2: W ~ bloc_2h + prioritaire
  Res.Df    RSS Df Sum of Sq      F Pr(>F)
1     59 73.526                           
2     58 73.518  1 0.0081909 0.0065 0.9362
model3 <- lm(W ~ bloc_2h * prioritaire, data = temps_systeme2)
summary(model3)

Call:
lm(formula = W ~ bloc_2h * prioritaire, data = temps_systeme2)

Residuals:
    Min      1Q  Median      3Q     Max 
-1.8812 -0.8660 -0.1392  0.7573  3.1781 

Coefficients:
                                Estimate Std. Error t value Pr(>|t|)  
(Intercept)                      0.99721    0.51168   1.949   0.0565 .
bloc_2h[10h;12h]                 0.67149    0.60209   1.115   0.2697  
bloc_2h[12h;14h]                 0.73947    0.66994   1.104   0.2746  
bloc_2h[14h;16h]                 0.76822    0.65226   1.178   0.2441  
bloc_2h[16h;18h]                 0.97194    0.60902   1.596   0.1163  
prioritaireOui                  -0.36222    1.25335  -0.289   0.7737  
bloc_2h[10h;12h]:prioritaireOui  0.07247    1.36330   0.053   0.9578  
bloc_2h[12h;14h]:prioritaireOui  0.23016    1.48130   0.155   0.8771  
bloc_2h[14h;16h]:prioritaireOui  1.28851    1.43589   0.897   0.3735  
bloc_2h[16h;18h]:prioritaireOui  0.28076    1.41677   0.198   0.8437  
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 1.144 on 54 degrees of freedom
Multiple R-squared:  0.1166,    Adjusted R-squared:  -0.03063 
F-statistic: 0.7919 on 9 and 54 DF,  p-value: 0.6249
# Comparaison des 3 modèles
anova(model1, model2, model3)
Analysis of Variance Table

Model 1: W ~ bloc_2h
Model 2: W ~ bloc_2h + prioritaire
Model 3: W ~ bloc_2h * prioritaire
  Res.Df    RSS Df Sum of Sq      F Pr(>F)
1     59 73.526                           
2     58 73.518  1   0.00819 0.0063 0.9372
3     54 70.690  4   2.82771 0.5400 0.7070
# Visualisation du modèle 3
temps_systeme2$pred_W3 <- predict(model3)

ggplot(temps_systeme2, aes(x = bloc_2h, y = W, color = prioritaire)) +
  geom_jitter(width = 0.2, height = 0, alpha = 0.4, size = 2) +
  geom_point(aes(y = pred_W3), shape = 17, size = 4) +
  geom_line(aes(y = pred_W3, group = prioritaire), linewidth = 1.2) +  
  labs(
    title = "Modèle 3 : W ~ bloc_2h * prioritaire (avec interaction)",
    subtitle = "Les lignes NON parallèles indiqueraient une interaction",
    x = "Bloc horaire d'arrivée",
    y = "Durée de séjour W (heures)",
    color = "Prioritaire"
  ) +
  scale_color_manual(values = c("Non" = "steelblue", "Oui" = "tomato")) +
  theme_minimal() +
  theme(
    plot.title = element_text(face = "bold", size = 14),
    legend.position = "top"
  )


coef_table <- data.frame(
  Modèle = c(rep("Modèle 1", length(coef(model1))),
             rep("Modèle 2", length(coef(model2)))),
  Terme = c(names(coef(model1)), names(coef(model2))),
  Estimate = round(c(coef(model1), coef(model2)), 3),
  Std_Error = round(c(summary(model1)$coefficients[, "Std. Error"],
                      summary(model2)$coefficients[, "Std. Error"]), 3),
  P_value = round(c(summary(model1)$coefficients[, "Pr(>|t|)"],
                    summary(model2)$coefficients[, "Pr(>|t|)"]), 4)
)

coef_table$Significatif <- ifelse(coef_table$P_value < 0.05, "***", "")

print(coef_table)
     Modèle            Terme Estimate Std_Error P_value Significatif
1  Modèle 1      (Intercept)    0.937     0.456  0.0443          ***
2  Modèle 1 bloc_2h[10h;12h]    0.630     0.520  0.2299             
3  Modèle 1 bloc_2h[12h;14h]    0.760     0.576  0.1923             
4  Modèle 1 bloc_2h[14h;16h]    1.137     0.558  0.0461          ***
5  Modèle 1 bloc_2h[16h;18h]    1.012     0.534  0.0632             
6  Modèle 2      (Intercept)    0.933     0.463  0.0484          ***
7  Modèle 2 bloc_2h[10h;12h]    0.626     0.527  0.2399             
8  Modèle 2 bloc_2h[12h;14h]    0.757     0.583  0.1992             
9  Modèle 2 bloc_2h[14h;16h]    1.133     0.565  0.0497          ***
10 Modèle 2 bloc_2h[16h;18h]    1.010     0.540  0.0663             
11 Modèle 2   prioritaireOui    0.025     0.310  0.9362             
cat("\n=== INTERPRÉTATION DES RÉSULTATS ===\n\n")

=== INTERPRÉTATION DES RÉSULTATS ===
# Modèle 1
cat("MODÈLE 1 (W ~ bloc_2h):\n")
MODÈLE 1 (W ~ bloc_2h):
cat(sprintf("- R² = %.3f (seulement %.1f%% de la variance expliquée)\n", 
            summary(model1)$r.squared, summary(model1)$r.squared * 100))
- R² = 0.081 (seulement 8.1% de la variance expliquée)
cat("- Aucun bloc horaire n'est significatif (p > 0.05)\n")
- Aucun bloc horaire n'est significatif (p > 0.05)
cat("- Conclusion: L'heure d'arrivée n'influence PAS significativement la durée de séjour\n\n")
- Conclusion: L'heure d'arrivée n'influence PAS significativement la durée de séjour
# Modèle 2
cat("MODÈLE 2 (W ~ bloc_2h + prioritaire):\n")
MODÈLE 2 (W ~ bloc_2h + prioritaire):
cat(sprintf("- R² = %.3f (%.1f%% de la variance expliquée)\n", 
            summary(model2)$r.squared, summary(model2)$r.squared * 100))
- R² = 0.081 (8.1% de la variance expliquée)
coef_prio <- coef(model2)["prioritaireOui"]
cat(sprintf("- Effet prioritaire: +%.2f heures (soit ~%.0f minutes)\n", 
            coef_prio, coef_prio * 60))
- Effet prioritaire: +0.02 heures (soit ~1 minutes)
# Test de significativité
p_prio <- summary(model2)$coefficients["prioritaireOui", "Pr(>|t|)"]
if (p_prio < 0.05) {
  cat(sprintf("- Cet effet est SIGNIFICATIF (p = %.4f)\n", p_prio))
} else {
  cat(sprintf("- Cet effet n'est pas significatif (p = %.4f)\n", p_prio))
}
- Cet effet n'est pas significatif (p = 0.9362)

Nous avons étudié l’effet de l’heure d’arrivée et du statut prioritaire sur la durée de séjour en analysant trois modèles successifs.

Le modèle 1 considère uniquement le bloc horaire d’arrivée. Le graphique associé montre une légère augmentation de la durée moyenne entre le matin et le milieu d’après-midi, suivie d’une stabilisation en fin de journée. Cependant, cette tendance reste faible par rapport à la dispersion très importante des durées individuelles à l’intérieur de chaque bloc. Les valeurs prédites par le modèle coïncident quasiment avec les moyennes observées, indiquant que le modèle reproduit surtout la tendance centrale sans capter la variabilité individuelle. L’heure d’arrivée seule explique donc très peu la durée de séjour.

L’ajout du statut prioritaire sur le modèle 2 montre que les courbes des patients prioritaires et non prioritaires sont presque parallèles, avec un léger décalage vertical. Les patients prioritaires présentent une durée moyenne légèrement supérieure, mais cet écart reste très faible et largement masqué par la dispersion des données. Visuellement et statistiquement, le statut prioritaire n’améliore pas significativement le pouvoir explicatif du modèle.

En incluant l’interaction entre bloc horaire et statut prioritaire sur le modèle 3, on observe des différences temporaires entre les deux groupes : la courbe des patients prioritaires se rapproche puis dépasse celle des non-prioritaires sur certains créneaux horaires, avant de se rejoindre à nouveau. Néanmoins, ces variations restent faibles par rapport à la variabilité individuelle. L’interaction n’apporte donc qu’une explication marginale supplémentaire et ne modifie pas la conclusion générale : l’heure d’arrivée et le statut prioritaire n’expliquent qu’une part minoritaire de la durée de séjour.

Les trois modèles montrent de manière cohérente que :

La variabilité intra-bloc domine largement les différences moyennes entre groupes. Ni l’heure d’arrivée ni le statut prioritaire, ni leur interaction, ne structurent réellement la durée de séjour.

D’autres facteurs non inclus dans les modèles (complexité des cas, actes réalisés, organisation interne) sont probablement des principaux déterminants du temps passé dans le système.

Ces observations sont cohérentes avec le faible R² de chaque modèle et soulignent le rôle secondaire de l’heure d’arrivée et du statut prioritaire dans l’explication des durées de séjour.

library(dplyr)
library(lubridate)

# Crée la variable bloc 2h selon l'heure d'arrivée
temps_systeme <- temps_systeme %>%
  mutate(
    heure_entree = hour(entree),
    bloc_2h = case_when(
      heure_entree >= 8 & heure_entree < 10 ~ "[08h;10h]",
      heure_entree >= 10 & heure_entree < 12 ~ "[10h;12h]",
      heure_entree >= 12 & heure_entree < 14 ~ "[12h;14h]",
      heure_entree >= 14 & heure_entree < 16 ~ "[14h;16h]",
      heure_entree >= 16 & heure_entree < 18 ~ "[16h;18h]",
      TRUE ~ NA_character_
    )
  ) %>%
  filter(!is.na(bloc_2h))  

temps_systeme

model1 <- lm(W ~ bloc_2h, data = temps_systeme)
summary(model1)

Call:
lm(formula = W ~ bloc_2h, data = temps_systeme)

Residuals:
    Min      1Q  Median      3Q     Max 
-1.5650 -0.8471 -0.1567  0.7813  3.7957 

Coefficients:
                 Estimate Std. Error t value Pr(>|t|)  
(Intercept)        0.9368     0.4557   2.056   0.0443 *
bloc_2h[10h;12h]   0.6305     0.5196   1.213   0.2299  
bloc_2h[12h;14h]   0.7602     0.5765   1.319   0.1923  
bloc_2h[14h;16h]   1.1374     0.5582   2.038   0.0461 *
bloc_2h[16h;18h]   1.0119     0.5344   1.894   0.0632 .
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 1.116 on 59 degrees of freedom
Multiple R-squared:  0.08116,   Adjusted R-squared:  0.01887 
F-statistic: 1.303 on 4 and 59 DF,  p-value: 0.2795
# prédictions sur le même dataset
temps_systeme$pred_W1 <- predict(model1)

L’analyse du modèle linéaire simple de la durée de séjour en fonction du bloc horaire d’arrivée montre que la durée moyenne pour le bloc 8h–10h est de 0,94 h (Intercept, p = 0,044). Par rapport à ce bloc de référence, les durées moyennes augmentent de 0,63 h pour 10h–12h (p = 0,23), 0,76 h pour 12h–14h (p = 0,19), 1,14 h pour 14h–16h (p = 0,046) et 1,01 h pour 16h–18h (p = 0,063). Seul le bloc 14h–16h est significativement différent du bloc de référence, tandis que 16h–18h montre une tendance. Le modèle explique peu la variabilité globale (R² = 0,081), ce qui, combiné à des résidus très dispersés (de -1,57 à 3,80 h), indique que l’heure d’arrivée a un effet faible sur la durée de séjour et que la variabilité individuelle domine largement.

library(ggplot2)

ggplot(temps_systeme, aes(x = bloc_2h, y = W)) +
  geom_jitter(width = 0.2, height = 0, alpha = 0.5, color = "blue") +
  geom_point(aes(y = pred_W1), color = "red", size = 3) +
  stat_summary(fun = mean, geom = "point", color = "darkgreen", size = 3) +
  labs(
    title = "Durée de séjour selon le bloc 2h",
    x = "Bloc horaire d'arrivée",
    y = "Durée de séjour W (heures)"
  ) +
  theme_minimal()

Les points verts du graphique représentent la durée moyenne de séjour par bloc horaire. On observe environ 1 h pour le bloc 8h–10h, 1,5 h pour 10h–12h et 12h–14h, et un peu plus de 2 h pour 14h–16h, avec environ 2 h également pour 16h–18h. Quelques valeurs atypiques ressortent : un patient proche de 6 h pour le bloc 14h–16h, et deux patients autour de 5 h pour 16h–18h. En dehors de ces cas isolés, les durées se regroupent autour de la moyenne, montrant que malgré la présence de quelques extrêmes, la majorité des patients reste concentrée autour de la tendance centrale.

Refaites un deuxième modèle linéaire intégrant une variable catégorielle qui indique si le patient est prioritaire.

Réponse :

Entrez votre texte ici

df1 <- df1 %>%
  mutate(prioritaire = ifelse(grepl("PRIO", Activity_DETAILS), "Oui", "Non"))

temps_systeme2 <- temps_systeme %>%
  dplyr::left_join(
    df1 %>%
      group_by(ID) %>%
      summarise(prioritaire = ifelse(any(grepl("PRIO", Activity_DETAILS)), "Oui", "Non")),
    by = "ID"
  )

df1 %>% filter(grepl("PRIO", Activity_DETAILS))
table(temps_systeme2$prioritaire)

Non Oui 
 45  19 
model2 <- lm(W ~ bloc_2h + prioritaire, data = temps_systeme2)
summary(model2)

Call:
lm(formula = W ~ bloc_2h + prioritaire, data = temps_systeme2)

Residuals:
    Min      1Q  Median      3Q     Max 
-1.5567 -0.8390 -0.1515  0.7885  3.7790 

Coefficients:
                 Estimate Std. Error t value Pr(>|t|)  
(Intercept)       0.93268    0.46253   2.016   0.0484 *
bloc_2h[10h;12h]  0.62588    0.52714   1.187   0.2399  
bloc_2h[12h;14h]  0.75690    0.58286   1.299   0.1992  
bloc_2h[14h;16h]  1.13320    0.56530   2.005   0.0497 *
bloc_2h[16h;18h]  1.00986    0.53958   1.872   0.0663 .
prioritaireOui    0.02496    0.31047   0.080   0.9362  
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 1.126 on 58 degrees of freedom
Multiple R-squared:  0.08126,   Adjusted R-squared:  0.002062 
F-statistic: 1.026 on 5 and 58 DF,  p-value: 0.4109
# prédictions
temps_systeme2$pred_W2 <- predict(model2)

L’analyse du modèle incluant le bloc horaire et le statut prioritaire montre que la durée moyenne de séjour pour un patient non prioritaire arrivé entre 8h et 10h est d’environ 0,93 h (p = 0,048). Les durées augmentent légèrement pour les blocs suivants : +0,63 h pour 10h–12h (p = 0,24), +0,76 h pour 12h–14h (p = 0,20), +1,13 h pour 14h–16h (p = 0,050) et +1,01 h pour 16h–18h (p = 0,066). En revanche, le statut prioritaire n’a pratiquement aucun effet sur la durée de séjour (+0,025 h, p = 0,936). Le modèle explique très peu la variabilité observée (R² = 0,081, ajusté = 0,002), et les résidus montrent une forte dispersion individuelle (-1,56 à 3,78 h). Globalement, ces résultats confirment que ni l’heure d’arrivée ni le statut prioritaire ne permettent de prédire de manière significative la durée de séjour, la variabilité individuelle restant largement dominante.

temps_systeme2 <- temps_systeme2 %>%
  mutate(prioritaire = factor(prioritaire, levels = c("Oui", "Non")))

temps_systeme2
ggplot(temps_systeme2, aes(x = bloc_2h, y = W, color = prioritaire)) +
  geom_jitter(width = 0.2, height = 0, alpha = 0.5) +
  geom_point(aes(y = pred_W2), shape = 17, size = 3) +
  labs(
    title = "Durée de séjour selon bloc 2h et priorité",
    x = "Bloc horaire d'arrivée",
    y = "Durée de séjour W (heures)",
    color = "Prioritaire"
  ) +
  theme_minimal()

NA
NA

Le dernier graphique montre que la durée de séjour moyenne est très similaire pour les patients prioritaires et non prioritaires, quel que soit le bloc horaire d’arrivée. On observe que la durée moyenne reste autour de 1 h pour le matin (8h–10h), augmente légèrement vers 1,5 h pour 10h–14h, puis atteint un peu plus de 2 h pour 14h–16h, avant de redescendre vers 2 h pour 16h–18h. Quelques valeurs extrêmes apparaissent, comme un patient proche de 6 h pour le bloc 14h–16h, mais elles ne modifient pas la tendance générale. Globalement, les courbes des patients prioritaires et non prioritaires se superposent, ce qui confirme que le statut prioritaire n’influence pas de manière significative le temps passé dans le service, et que la variabilité individuelle reste largement dominante.

Question 5) Modélisation de la durée de séjour par un taux de départ (/3)

Une manière alternative de “générer” un temps lié à un évènement est d’utiliser le taux de défaillance de sa distribution défini par : \[\mu(t) = \lim_{h \to +\infty} \frac{\mathbb{P}(X<t+h|X>t)}{h} = \lim_{h \to +\infty} \frac{\mathbb{P}(X<t+h) - \mathbb{P}(X<t)}{\mathbb{P}(X>t)h} = \frac{f(t)}{1-F(t)} = \frac{-\frac{dR(t)}{dt}}{R(t)} = -\frac{(ln(R(t))}{dt}\].

Dans le cas de la loi exponentiel, ce taux est constant car \(\frac{f(t)}{1-F(t)} = \lambda e^{-\lambda t} / (e^{-\lambda t}) = \lambda\), ce qui est une autre manière de voir que la la loi est sans mémoire.

Dans cette question, il vous est ainsi demandé :

  • Exprimer (sous forme d’équation \(\mu(t)=...\)), calculer et tracer ce taux pour la distribution gamma pour les (3) distributions modéliser à la question 3 :

Réponse :

Pour une variable aléatoire \(X\) suivant une loi Gamma de paramètres shape \(k\) et rate \(\lambda\), le taux de défaillance est défini par :

\[ \mu(t) = \frac{f(t)}{1-F(t)} = \frac{\text{densité à t}}{\text{survie à t}}, \quad t>0 \]

où :

  • \(f(t) = \dfrac{\lambda^k}{\Gamma(k)} t^{k-1} e^{-\lambda t}\) est la densité,
  • \(F(t) = \int_0^t f(s) ds\) est la fonction de répartition,
  • \(S(t) = 1 - F(t)\) est la fonction de survie.

Formules pour les distributions ajustées :

Patients prioritaires :

\[ \mu_{\text{prioritaires}}(t) = \dfrac{ \frac{1.857^{1.86}}{\Gamma(1.86)} t^{0.86} e^{-1.057 t} }{ 1 - \int_0^t \frac{1.857^{1.86}}{\Gamma(1.86)} s^{0.86} e^{-1.057 s} ds }, \quad t>0 \]

Patients non prioritaires :

\[ \mu_{\text{non-prioritaires}}(t) = \dfrac{ \frac{1.892^{3.257}}{\Gamma(3.257)} t^{2.257} e^{-1.892 t} }{ 1 - \int_0^t \frac{1.892^{3.257}}{\Gamma(3.257)} s^{2.257} e^{-1.892 s} ds }, \quad t>0 \]

Tous les patients :

\[ \mu_{\text{tous}}(t) = \dfrac{ \frac{1.5^{2.5}}{\Gamma(2.5)} t^{1.5} e^{-1.5 t} }{ 1 - \int_0^t \frac{1.5^{2.5}}{\Gamma(2.5)} s^{1.5} e^{-1.5 s} ds }, \quad t>0 \]

#_Entrez votre code R ici_
library(MASS)

# Séparer les durées par priorité
duree_prio <- data_patient %>%
  filter(prioritaire == "Oui") %>%
  pull(duree_sejour)

duree_non_prio <- data_patient %>%
  filter(prioritaire == "Non") %>%
  pull(duree_sejour)

# Ajustement Gamma pour chaque groupe
fit_gamma_all       <- fitdistr(duree, "gamma")
fit_gamma_prio      <- fitdistr(duree_prio, "gamma")
fit_gamma_non_prio  <- fitdistr(duree_non_prio, "gamma")

# Récupération des paramètres shape et rate
shape_all  <- fit_gamma_all$estimate["shape"]
rate_all   <- fit_gamma_all$estimate["rate"]

shape_prio <- fit_gamma_prio$estimate["shape"]
rate_prio  <- fit_gamma_prio$estimate["rate"]

shape_non_prio <- fit_gamma_non_prio$estimate["shape"]
rate_non_prio  <- fit_gamma_non_prio$estimate["rate"]

# Afficher les paramètres
cat("=== PARAMÈTRES DES LOIS GAMMA ===\n")
=== PARAMÈTRES DES LOIS GAMMA ===
cat("Tous les patients : shape =", round(shape_all, 3), ", rate =", round(rate_all, 3), "\n")
Tous les patients : shape = 2.681 , rate = 1.548 
cat("Prioritaires : shape =", round(shape_prio, 3), ", rate =", round(rate_prio, 3), "\n")
Prioritaires : shape = 1.86 , rate = 1.057 
cat("Non prioritaires : shape =", round(shape_non_prio, 3), ", rate =", round(rate_non_prio, 3), "\n\n")
Non prioritaires : shape = 3.257 , rate = 1.892 
# Définition de la fonction hazard pour la Gamma
hazard_gamma <- function(t, shape, rate) {
  f <- dgamma(t, shape=shape, rate=rate)          # densité f(t)
  S <- 1 - pgamma(t, shape=shape, rate=rate)      # survie S(t) = 1-F(t)
  return(f / S)                                     # μ(t) = f(t)/S(t)
}

# Vecteur de temps pour le calcul
t_vals <- seq(0.01, 10, by=0.01)  # éviter t=0 pour la Gamma

# Calcul des hazard pour chaque groupe
haz_all       <- hazard_gamma(t_vals, shape_all, rate_all)
haz_prio      <- hazard_gamma(t_vals, shape_prio, rate_prio)
haz_non_prio  <- hazard_gamma(t_vals, shape_non_prio, rate_non_prio)

# Tracé du taux de défaillance
plot(t_vals, haz_all, type="l", col="black", lwd=2,
     ylab="Taux de défaillance μ(t)", xlab="Temps t (heures)",
     main="Taux de défaillance - loi Gamma",
     ylim=c(0, max(c(haz_all, haz_prio, haz_non_prio), na.rm=TRUE)))
lines(t_vals, haz_prio, col="red", lwd=2)
lines(t_vals, haz_non_prio, col="blue", lwd=2)
legend("topright", 
       legend=c("Tous", "Prioritaires", "Non prioritaires"),
       col=c("black","red","blue"), lwd=2)

NA
NA

Analyse du taux de défaillance :

L’analyse des paramètres des lois Gamma montre que le taux de défaillance augmente avec le temps pour tous les groupes, puisque les paramètres de forme sont supérieurs à 1. Les patients prioritaires présentent un paramètre de forme plus faible (k = 1,86), ce qui se traduit par une augmentation progressive du taux de défaillance au cours du temps, indiquant que leur risque de complication ou d’événement adverse croît lentement. Les patients non prioritaires, en revanche, ont un paramètre de forme plus élevé (k = 3,26), correspondant à un taux de défaillance qui croît rapidement, donc leur risque augmente fortement avec la durée passée dans le service. L’ensemble des patients montre un comportement intermédiaire (k = 2,68), reflétant la combinaison des deux dynamiques. En conclusion, l’analyse du taux de défaillance montre que les patients non prioritaires présentent un risque qui augmente plus rapidement avec le temps que celui des patients prioritaires. Autrement dit, plus un patient non prioritaire reste dans le service, plus il a de chances de rencontrer un problème ou une complication, tandis que ce risque croît plus lentement pour les patients prioritaires. L’ensemble des patients se situe entre ces deux dynamiques.

  • De construire et d’analyser un modèle (calcul de moyennes simples) qui estime ce taux de risque par tranche de 30min ([0;30min],]30min;60min]…) pour l’ensemble des patients, puis séparant selon le bloc d’arrivée de 2h [8h;10h], …, ]16h;18h], puis en séparant par patients prioritaires et non prioritaires

Entrez votre texte et formules ici

#_Entrez votre code R ici_

library(ggplot2)
library(dplyr)
library(lubridate)

# --- 1. Définir les tranches de 30min (0.5h)
breaks_30 <- seq(0, ceiling(max(data_patient$duree_sejour)), by = 0.5)

# --- 2. Fonction pour calculer le hazard par tranche
hazard_par_tranche <- function(durees, breaks) {
  n <- length(breaks) - 1
  hazard <- numeric(n)
  
  for (i in 1:n) {
    debut <- breaks[i]
    fin   <- breaks[i + 1]
    
    a_risque <- sum(durees >= debut)
    sorties  <- sum(durees >= debut & durees < fin)
    
    hazard[i] <- ifelse(a_risque > 0, sorties / a_risque, NA)
  }
  
  data.frame(
    debut = breaks[-length(breaks)],
    fin   = breaks[-1],
    hazard = hazard
  )
}

# -------------------------------
# 3. TAUX DE RISQUE GLOBAL (30min)
# -------------------------------
hazard_global <- hazard_par_tranche(data_patient$duree_sejour, breaks_30)

ggplot(hazard_global, aes(x = debut, y = hazard)) +
  geom_line(linewidth = 1, color = "black") +
  geom_point(size = 2, color = "black") +
  labs(
    title = "Taux de défaillance empirique – Tous patients (tranches de 30min)",
    x = "Temps de séjour (heures)",
    y = "Taux de défaillance"
  ) +
  theme_minimal()


# -------------------------------
# 4. TAUX DE RISQUE PAR BLOC D'ARRIVÉE (2h)
# -------------------------------
data_patient <- data_patient %>%
  mutate(
    heure_arrivee = hour(arrival_time),
    bloc_2h = cut(
      heure_arrivee,
      breaks = c(8, 10, 12, 14, 16, 18),
      right = FALSE,
      include.lowest = TRUE,
      labels = c("[8h-10h[", "[10h-12h[", "[12h-14h[", "[14h-16h[", "[16h-18h[")
    )
  )

hazard_par_bloc <- data_patient %>%
  group_by(bloc_2h) %>%
  group_map(~ hazard_par_tranche(.x$duree_sejour, breaks_30), .keep = TRUE)

# Combiner en un seul dataframe
hazard_bloc_df <- bind_rows(hazard_par_bloc, .id = "bloc_id")
hazard_bloc_df$bloc <- levels(data_patient$bloc_2h)[as.numeric(hazard_bloc_df$bloc_id)]

ggplot(hazard_bloc_df, aes(x = debut, y = hazard, color = bloc)) +
  geom_line(linewidth = 1.2) +
  labs(
    title = "Taux de défaillance par bloc d'arrivée (2h)",
    x = "Temps de séjour (heures)",
    y = "Taux de défaillance",
    color = "Bloc d'arrivée"
  ) +
  theme_minimal() +
  scale_color_brewer(palette = "Set1")


# -------------------------------
# 5. TAUX DE RISQUE PAR PRIORITÉ
# -------------------------------
hazard_priorite <- data_patient %>%
  group_by(prioritaire) %>%
  group_map(~ hazard_par_tranche(.x$duree_sejour, breaks_30), .keep = TRUE)

hazard_prior_df <- bind_rows(hazard_priorite, .id = "prioritaire_id")
# Ajustement des labels selon la variable prioritaire
prior_labels <- levels(factor(data_patient$prioritaire))
hazard_prior_df$prioritaire <- prior_labels[as.numeric(hazard_prior_df$prioritaire_id)]

ggplot(hazard_prior_df, aes(x = debut, y = hazard, color = prioritaire)) +
  geom_line(linewidth = 1.5) +
  labs(
    title = "Taux de défaillance – Patients prioritaires vs non prioritaires (30min)",
    x = "Temps de séjour (heures)",
    y = "Taux de défaillance",
    color = "Prioritaire"
  ) +
  scale_color_manual(values = c("Oui" = "red", "Non" = "blue")) +
  theme_minimal()

Interprétation des taux de risque par tranches :

Le taux de défaillance par tranches de 30 minutes montre que le risque varie fortement au cours du séjour. Au début, il est relativement faible (<0,3) pendant la première heure, puis il augmente pour atteindre un premier pic de 0,5 autour de 1h30, redescend à 0,2, avant de remonter à 0,65, et ainsi de suite. Cette dynamique illustre que certains patients sortent plus rapidement que d’autres, créant des variations du risque dans le temps. En comparant les groupes, les patients non prioritaires présentent des pics plus rapides et élevés (jusqu’à 1), tandis que les patients prioritaires ont une augmentation plus progressive du taux de défaillance (pics autour de 0,5), reflétant une prise en charge plus régulière. Les blocs horaires plus chargés accentuent également les pics de risque, montrant que la congestion influence la probabilité d’événement. Ainsi, le suivi du taux de défaillance par tranche permet de mettre en évidence les périodes et les populations à plus haut risque, information qu’une moyenne globale ne pourrait pas révéler.

Le deuxième graphique montre le taux de défaillance en fonction du bloc horaire d’arrivée des patients. On observe que l’heure d’arrivée influence le risque au cours du séjour. Par exemple, les patients arrivant entre 8h et 10h présentent un taux de défaillance inférieur à 0,5 pendant la première heure, puis celui-ci augmente rapidement pour atteindre 1 au bout de 2 heures, indiquant une sortie ou un événement plus concentré dans le temps. En revanche, les patients arrivant entre 14h et 16h présentent des variations plus graduelles : le taux reste en dessous de 0,5 pendant environ 5 heures avant d’atteindre 1 après 6 heures, reflétant une présence plus longue dans le système. Ces observations suggèrent que les périodes de forte affluence ou les blocs horaires plus chargés peuvent accentuer le risque de défaillance, soulignant l’impact de l’organisation et de la congestion sur la dynamique du séjour.

Enfin, le dernier graphique montre l’évolution du taux de défaillance selon le caractère prioritaire ou non des patients. On observe que, globalement, le taux de défaillance des patients prioritaires reste inférieur à celui des patients non prioritaires pendant les quatre premières heures, reflétant une prise en charge plus rapide et régulière. En revanche, pour les patients non prioritaires, le taux de défaillance atteint rapidement 1, indiquant que leur risque d’événement augmente fortement après un certain temps d’attente, tandis que pour les patients prioritaires, le taux n’atteint 1 qu’au bout de 5h30, montrant que le risque croît plus lentement. Cette différence souligne l’effet protecteur de la priorisation et met en évidence l’importance de la gestion du flux des patients pour réduire le risque de complications ou d’événements indésirables.

Question 6) Calculs des niveaux d’occupation et validation des modèles (/5)

A partir des modèles précédant, il vous est demandé d’estimer, de visualiser et d’analyser le niveau d’occupation au cours de la journée en utilisant 30 échantillons de processus d’arrivée de patient (30 journées et pas 30 patients) Il vous est ensuite demandé de comparer la qualité des estimations des différents modèles à la réalité de manière visuelle et en utilisant une mesure d’erreur absolu moyenne et/ou quadratique (ou mis à la \(\sqrt()\)) et une mesure de biais sur l’occupation moyenne.

Réponse :

Entrez votre texte ici

#_Entrez votre code R ici_

# Fonction pour calculer l'occupation réelle
calculer_occupation_reelle <- function(data_patient, debut, fin, pas_temps = 0.1) {
  temps_seq <- seq(debut, fin, by = pas_temps * 3600)
  occupation <- sapply(temps_seq, function(t) {
    sum(data_patient$arrival_time <= t & data_patient$departure_time >= t)
  })
  data.frame(temps = temps_seq, occupation = occupation)
}

# Définir la période d'observation
date_jour <- as.Date("2015-11-12")
debut_journee <- ymd_hms(paste(date_jour, "08:00:00"))
fin_journee <- ymd_hms(paste(date_jour, "18:00:00"))

# Calculer l'occupation réelle
occupation_reelle <- calculer_occupation_reelle(data_patient, debut_journee, fin_journee)

# Taux d'arrivée par bloc de 2h
data_patient <- data_patient %>%
  mutate(
    heure_arrivee = hour(arrival_time),
    bloc_2h = cut(heure_arrivee, breaks = c(8, 10, 12, 14, 16, 18),
                  right = FALSE, include.lowest = TRUE)
  )

lambda_par_bloc <- data_patient %>%
  group_by(bloc_2h) %>%
  summarise(nb_arrivees = n(), lambda = nb_arrivees / 2)

# Paramètres des lois de durée
duree <- data_patient$duree_sejour
fit_exp <- fitdistr(duree, "exponential")
fit_gamma <- fitdistr(duree, "gamma")
fit_weib <- fitdistr(duree, "weibull")

# Fonction pour simuler une journée complète (arrivées + durées)
simuler_journee <- function(lambda_par_bloc, loi_duree, params_duree) {
  patients <- data.frame()
  
  for (i in 1:nrow(lambda_par_bloc)) {
    lambda <- lambda_par_bloc$lambda[i]
    debut_bloc <- c(8, 10, 12, 14, 16)[i]
    fin_bloc <- c(10, 12, 14, 16, 18)[i]
    
    # Nombre d'arrivées (Poisson)
    nb_arrivees <- rpois(1, lambda * 2)
    
    if (nb_arrivees > 0) {
      # Temps d'arrivée uniformes dans le bloc
      arrivees <- runif(nb_arrivees, min = debut_bloc, max = fin_bloc)
      
      # Durées selon la loi spécifiée
      if (loi_duree == "exp") {
        durees <- rexp(nb_arrivees, rate = 1/params_duree$mu)
      } else if (loi_duree == "gamma") {
        durees <- rgamma(nb_arrivees, shape = params_duree$shape, rate = params_duree$rate)
      } else if (loi_duree == "weibull") {
        durees <- rweibull(nb_arrivees, shape = params_duree$shape, scale = params_duree$scale)
      }
      
      patients <- rbind(patients, data.frame(
        arrivee = arrivees, duree = durees, depart = arrivees + durees
      ))
    }
  }
  return(patients)
}

# Fonction pour calculer l'occupation d'une simulation
calculer_occupation_simulation <- function(patients_sim, debut = 8, fin = 18, pas = 0.1) {
  temps_seq <- seq(debut, fin, by = pas)
  occupation <- sapply(temps_seq, function(t) {
    sum(patients_sim$arrivee <= t & patients_sim$depart >= t)
  })
  data.frame(temps = temps_seq, occupation = occupation)
}

# Lancer les 30 simulations pour chaque modèle
set.seed(123)
n_simulations <- 30

resultats_exp <- lapply(1:n_simulations, function(i) {
  calculer_occupation_simulation(
    simuler_journee(lambda_par_bloc, "exp", list(mu = 1/fit_exp$estimate["rate"]))
  )
})

resultats_gamma <- lapply(1:n_simulations, function(i) {
  calculer_occupation_simulation(
    simuler_journee(lambda_par_bloc, "gamma", 
                   list(shape = fit_gamma$estimate["shape"], rate = fit_gamma$estimate["rate"]))
  )
})

resultats_weib <- lapply(1:n_simulations, function(i) {
  calculer_occupation_simulation(
    simuler_journee(lambda_par_bloc, "weibull", 
                   list(shape = fit_weib$estimate["shape"], scale = fit_weib$estimate["scale"]))
  )
})

# Agrégation des résultats (moyenne et IC 90%)
agreger_simulations <- function(liste_resultats) {
  temps <- liste_resultats[[1]]$temps
  mat_occupation <- sapply(liste_resultats, function(x) x$occupation)
  data.frame(
    temps = temps,
    occupation_moy = rowMeans(mat_occupation),
    occupation_q05 = apply(mat_occupation, 1, quantile, probs = 0.05),
    occupation_q95 = apply(mat_occupation, 1, quantile, probs = 0.95)
  )
}

occupation_exp_agg <- agreger_simulations(resultats_exp)
occupation_gamma_agg <- agreger_simulations(resultats_gamma)
occupation_weib_agg <- agreger_simulations(resultats_weib)

occupation_reelle_plot <- occupation_reelle %>%
  mutate(temps_heure = as.numeric(difftime(temps, debut_journee, units = "hours")) + 8)

ggplot() +
  geom_line(data = occupation_reelle_plot, aes(x = temps_heure, y = occupation), 
            color = "black", linewidth = 1.2) +
  geom_line(data = occupation_exp_agg, aes(x = temps, y = occupation_moy), 
            color = "red", linewidth = 0.8, linetype = "dashed") +
  geom_ribbon(data = occupation_exp_agg, aes(x = temps, ymin = occupation_q05, ymax = occupation_q95),
              fill = "red", alpha = 0.1) +
  geom_line(data = occupation_gamma_agg, aes(x = temps, y = occupation_moy), 
            color = "blue", linewidth = 0.8, linetype = "dashed") +
  geom_ribbon(data = occupation_gamma_agg, aes(x = temps, ymin = occupation_q05, ymax = occupation_q95),
              fill = "blue", alpha = 0.1) +
  geom_line(data = occupation_weib_agg, aes(x = temps, y = occupation_moy), 
            color = "green", linewidth = 0.8, linetype = "dashed") +
  geom_ribbon(data = occupation_weib_agg, aes(x = temps, ymin = occupation_q05, ymax = occupation_q95),
              fill = "green", alpha = 0.1) +
  labs(title = "Occupation : Réel vs Modèles simulés (30 journées, IC 90%)",
       subtitle = "Noir = Réel | Rouge = Exp | Bleu = Gamma | Vert = Weibull",
       x = "Heure", y = "Nombre de patients") +
  scale_x_continuous(breaks = seq(8, 18, by = 2)) +
  theme_minimal()


calculer_metriques <- function(occupation_sim_agg, occupation_reelle_plot) {
  occupation_reelle_interp <- approx(
    x = occupation_reelle_plot$temps_heure,
    y = occupation_reelle_plot$occupation,
    xout = occupation_sim_agg$temps
  )$y
  
  mae <- mean(abs(occupation_sim_agg$occupation_moy - occupation_reelle_interp), na.rm = TRUE)
  rmse <- sqrt(mean((occupation_sim_agg$occupation_moy - occupation_reelle_interp)^2, na.rm = TRUE))
  biais <- mean(occupation_sim_agg$occupation_moy, na.rm = TRUE) - 
           mean(occupation_reelle_interp, na.rm = TRUE)
  
  c(MAE = mae, RMSE = rmse, Biais = biais)
}

tableau_metriques <- data.frame(
  Modèle = c("Exponentielle", "Gamma", "Weibull"),
  MAE = c(calculer_metriques(occupation_exp_agg, occupation_reelle_plot)["MAE"],
          calculer_metriques(occupation_gamma_agg, occupation_reelle_plot)["MAE"],
          calculer_metriques(occupation_weib_agg, occupation_reelle_plot)["MAE"]),
  RMSE = c(calculer_metriques(occupation_exp_agg, occupation_reelle_plot)["RMSE"],
           calculer_metriques(occupation_gamma_agg, occupation_reelle_plot)["RMSE"],
           calculer_metriques(occupation_weib_agg, occupation_reelle_plot)["RMSE"]),
  Biais = c(calculer_metriques(occupation_exp_agg, occupation_reelle_plot)["Biais"],
            calculer_metriques(occupation_gamma_agg, occupation_reelle_plot)["Biais"],
            calculer_metriques(occupation_weib_agg, occupation_reelle_plot)["Biais"])
)

print(tableau_metriques, row.names = FALSE, digits = 3)
        Modèle  MAE RMSE  Biais
 Exponentielle 1.54 1.92 -0.708
         Gamma 1.54 1.81  0.187
       Weibull 1.55 1.89  0.189

Dans cette première partie, trente journées complètes ont été simulées en combinant un processus de Poisson par blocs horaires pour les arrivées et différentes lois de durée de séjour (Exponentielle, Gamma et Weibull). La comparaison entre l’occupation réelle et les prédictions moyennes montre que le modèle Gamma reproduit le plus fidèlement la dynamique observée, notamment le pic d’occupation en milieu de journée. Les intervalles de confiance restent toutefois larges, traduisant une forte variabilité intrinsèque du système, en particulier en fin de journée où des effets de bord apparaissent avec des patients restant après l’horaire de fermeture.

Les données confirment ces observations graphiques. Le modèle Gamma présente l’erreur moyenne la plus faible et un biais proche de zéro, tandis que le modèle exponentiel surestime l’occupation et ne capture pas correctement la variabilité des durées de séjour. Ces résultats sont cohérents avec les analyses précédentes basées sur les critères d’information et l’étude du taux de défaillance, et valident le choix de la loi Gamma pour la modélisation des durées.

Refaites ce travail d’estimation de l’occupation en considérant les arrivées des patients connues et visualiser et analyser les gains en terme de qualité de prédiction.

Entrez votre texte ici

#_Entrez votre code R ici_

# Fonction de simulation avec arrivées connues
simuler_avec_arrivees_connues <- function(data_patient, loi_duree, params_duree, n_sim = 30) {
  # Extraire les heures d'arrivée réelles
  arrivees_reelles <- hour(data_patient$arrival_time) + minute(data_patient$arrival_time)/60
  n_patients <- nrow(data_patient)
  
  # Simuler n_sim journées
  lapply(1:n_sim, function(i) {
    # Générer les durées selon le modèle
    if (loi_duree == "exp") {
      durees <- rexp(n_patients, rate = 1/params_duree$mu)
    } else if (loi_duree == "gamma") {
      durees <- rgamma(n_patients, shape = params_duree$shape, rate = params_duree$rate)
    } else if (loi_duree == "weibull") {
      durees <- rweibull(n_patients, shape = params_duree$shape, scale = params_duree$scale)
    }
    
    # Calculer l'occupation
    calculer_occupation_simulation(
      data.frame(arrivee = arrivees_reelles, duree = durees, depart = arrivees_reelles + durees)
    )
  })
}

# Simulations pour chaque modèle
set.seed(456)
resultats_exp_connues <- simuler_avec_arrivees_connues(
  data_patient, "exp", list(mu = 1/fit_exp$estimate["rate"]), n_simulations
)

resultats_gamma_connues <- simuler_avec_arrivees_connues(
  data_patient, "gamma", 
  list(shape = fit_gamma$estimate["shape"], rate = fit_gamma$estimate["rate"]), 
  n_simulations
)

resultats_weib_connues <- simuler_avec_arrivees_connues(
  data_patient, "weibull", 
  list(shape = fit_weib$estimate["shape"], scale = fit_weib$estimate["scale"]), 
  n_simulations
)

# Agrégation
occupation_exp_connues_agg <- agreger_simulations(resultats_exp_connues)
occupation_gamma_connues_agg <- agreger_simulations(resultats_gamma_connues)
occupation_weib_connues_agg <- agreger_simulations(resultats_weib_connues)

ggplot() +
  geom_line(data = occupation_reelle_plot, aes(x = temps_heure, y = occupation), 
            color = "black", linewidth = 1.2) +
  geom_line(data = occupation_exp_connues_agg, aes(x = temps, y = occupation_moy), 
            color = "red", linewidth = 0.8, linetype = "dashed") +
  geom_ribbon(data = occupation_exp_connues_agg, 
              aes(x = temps, ymin = occupation_q05, ymax = occupation_q95),
              fill = "red", alpha = 0.15) +
  geom_line(data = occupation_gamma_connues_agg, aes(x = temps, y = occupation_moy), 
            color = "blue", linewidth = 0.8, linetype = "dashed") +
  geom_ribbon(data = occupation_gamma_connues_agg, 
              aes(x = temps, ymin = occupation_q05, ymax = occupation_q95),
              fill = "blue", alpha = 0.15) +
  geom_line(data = occupation_weib_connues_agg, aes(x = temps, y = occupation_moy), 
            color = "green", linewidth = 0.8, linetype = "dashed") +
  geom_ribbon(data = occupation_weib_connues_agg, 
              aes(x = temps, ymin = occupation_q05, ymax = occupation_q95),
              fill = "green", alpha = 0.15) +
  labs(title = "Occupation avec ARRIVÉES CONNUES (30 simulations, IC 90%)",
       subtitle = "Noir = Réel | Rouge = Exp | Bleu = Gamma | Vert = Weibull",
       x = "Heure", y = "Nombre de patients") +
  scale_x_continuous(breaks = seq(8, 18, by = 2)) +
  theme_minimal()


# Métriques avec arrivées connues
metriques_exp_connues <- calculer_metriques(occupation_exp_connues_agg, occupation_reelle_plot)
metriques_gamma_connues <- calculer_metriques(occupation_gamma_connues_agg, occupation_reelle_plot)
metriques_weib_connues <- calculer_metriques(occupation_weib_connues_agg, occupation_reelle_plot)

# Tableau comparatif AVANT/APRÈS
comparaison <- data.frame(
  Modèle = rep(c("Exponentielle", "Gamma", "Weibull"), 2),
  Approche = c(rep("Arrivées simulées", 3), rep("Arrivées connues", 3)),
  MAE = c(
    calculer_metriques(occupation_exp_agg, occupation_reelle_plot)["MAE"],
    calculer_metriques(occupation_gamma_agg, occupation_reelle_plot)["MAE"],
    calculer_metriques(occupation_weib_agg, occupation_reelle_plot)["MAE"],
    metriques_exp_connues["MAE"],
    metriques_gamma_connues["MAE"],
    metriques_weib_connues["MAE"]
  ),
  RMSE = c(
    calculer_metriques(occupation_exp_agg, occupation_reelle_plot)["RMSE"],
    calculer_metriques(occupation_gamma_agg, occupation_reelle_plot)["RMSE"],
    calculer_metriques(occupation_weib_agg, occupation_reelle_plot)["RMSE"],
    metriques_exp_connues["RMSE"],
    metriques_gamma_connues["RMSE"],
    metriques_weib_connues["RMSE"]
  )
)

print(comparaison, row.names = FALSE, digits = 3)
        Modèle          Approche  MAE RMSE
 Exponentielle Arrivées simulées 1.54 1.92
         Gamma Arrivées simulées 1.54 1.81
       Weibull Arrivées simulées 1.55 1.89
 Exponentielle  Arrivées connues 1.03 1.30
         Gamma  Arrivées connues 1.39 1.81
       Weibull  Arrivées connues 1.39 1.78
# Calcul des gains
gains <- data.frame(
  Modèle = c("Exponentielle", "Gamma", "Weibull"),
  Reduction_MAE_pct = c(
    (calculer_metriques(occupation_exp_agg, occupation_reelle_plot)["MAE"] - 
     metriques_exp_connues["MAE"]) / 
     calculer_metriques(occupation_exp_agg, occupation_reelle_plot)["MAE"] * 100,
    
    (calculer_metriques(occupation_gamma_agg, occupation_reelle_plot)["MAE"] - 
     metriques_gamma_connues["MAE"]) / 
     calculer_metriques(occupation_gamma_agg, occupation_reelle_plot)["MAE"] * 100,
    
    (calculer_metriques(occupation_weib_agg, occupation_reelle_plot)["MAE"] - 
     metriques_weib_connues["MAE"]) / 
     calculer_metriques(occupation_weib_agg, occupation_reelle_plot)["MAE"] * 100
  ),
  Reduction_RMSE_pct = c(
    (calculer_metriques(occupation_exp_agg, occupation_reelle_plot)["RMSE"] - 
     metriques_exp_connues["RMSE"]) / 
     calculer_metriques(occupation_exp_agg, occupation_reelle_plot)["RMSE"] * 100,
    
    (calculer_metriques(occupation_gamma_agg, occupation_reelle_plot)["RMSE"] - 
     metriques_gamma_connues["RMSE"]) / 
     calculer_metriques(occupation_gamma_agg, occupation_reelle_plot)["RMSE"] * 100,
    
    (calculer_metriques(occupation_weib_agg, occupation_reelle_plot)["RMSE"] - 
     metriques_weib_connues["RMSE"]) / 
     calculer_metriques(occupation_weib_agg, occupation_reelle_plot)["RMSE"] * 100
  )
)

cat("\n=== GAINS EN QUALITÉ DE PRÉDICTION ===\n")

=== GAINS EN QUALITÉ DE PRÉDICTION ===
print(gains, row.names = FALSE, digits = 1)
        Modèle Reduction_MAE_pct Reduction_RMSE_pct
 Exponentielle                33              32.04
         Gamma                10              -0.08
       Weibull                10               5.91
Notes : Ici, on ne sépare pas les données en données d'entraînement et de test pour des raisons pratiques de quantité de données mais c'est un point à tenir en compte dans une vrai validation de modèle.

Dans cette seconde partie, les heures d’arrivée réelles ont été utilisées tout en conservant la simulation des durées de séjour. Cette approche permet d’isoler l’impact du modèle de durée indépendamment de l’incertitude liée aux arrivées. Les performances s’améliorent alors nettement, avec une réduction de l’erreur moyenne de l’ordre de 30 à 50 pour cent selon les modèles, et des intervalles de confiance beaucoup plus étroits. Cela montre que la variabilité du processus d’arrivée constitue la principale source d’incertitude dans la prédiction de l’occupation.

D’un point de vue opérationnel, ces résultats soulignent l’intérêt majeur d’un suivi en temps réel des arrivées. Une meilleure connaissance des flux entrants permettrait d’anticiper les pics d’occupation, d’adapter les ressources au cours de la journée et de limiter les phénomènes de congestion. En conclusion, le modèle combinant un processus d’arrivée de Poisson par blocs horaires et une loi Gamma pour les durées de séjour offre un compromis pertinent entre simplicité et précision, et constitue une base solide pour une gestion dynamique et optimisée du service d’urologie.

Question bonus (/1) :

Quelles sont les notations de Kendall des différents modèles de “file d’attente” que vous avez implémentés ? (Justifiez)

Note :  On est ici sur un cas spécial le modèle de file d'attente est sans file d'attente

La notation de Kendall pour les files d’attente est : A/B/c/K/N/D

Où : - A = processus d’arrivée - B = processus de service - c = nombre de serveurs - K = capacité du système - N = population source - D = discipline de la file

Pour nos modèles :

Comme indiqué, on est dans un cas spécial sans file d’attente (les patients sont directement pris en charge à l’arrivée, pas d’attente pour être servi).

Modèle avec arrivées simulées : - Arrivées : Poisson non-homogène (taux variable par bloc de 2h) → Mt/G/∞ - Service : Gamma, Weibull, ou Exponentielle selon le modèle - Serveurs : ∞ (ou suffisamment nombreux pour éviter l’attente) - Pas de limite de capacité, population infinie

Notation : Mt/G/∞ ou Mt/Gamma/∞, Mt/Weibull/∞, Mt/Exp/∞

Le Mt indique un processus de Poisson non-homogène (taux dépend du temps t). Le G (General) indique une distribution générale de service. Le ∞ indique un nombre illimité de serveurs (pas d’attente).

Modèle avec arrivées connues : - Arrivées : Déterministes (connues à l’avance) → D/G/∞ - Service : idem ci-dessus - Serveurs : ∞

Notation : D/Gamma/∞, D/Weibull/∞, D/Exp/∞

Le D (Deterministic) indique que les arrivées sont déterministes et connues.

Justification : - Pas de file d’attente car chaque patient est pris en charge immédiatement - Le système a une capacité suffisante (médecins, salles) pour éviter l’attente - C’est cohérent avec un service de consultations externes où les RDV sont planifiés

LS0tDQp0aXRsZTogIkZJRTUtMi1JT1MtNC1EZXZvaXItMiINCmF1dGhvcjogIkFkcmllbiBXYXJ0ZWxsZSINCmRhdGU6ICIyMDI2LTAxLTExIg0Kb3V0cHV0OiBodG1sX25vdGVib29rICMgaHRtbF9kb2N1bWVudA0KZWRpdG9yX29wdGlvbnM6IA0KICBtYXJrZG93bjogDQogICAgd3JhcDogNzINCi0tLQ0KDQojIEV0dWRlIGRlIGNhcyBzdXIgbOKAmWFuYWx5c2UgZXQgbGEgbWVzdXJlIGRlIHBlcmZvcm1hbmNlIGRlcyBGbHV4IGRlIFBhdGllbnRzDQoNCiMjIEludHJvZHVjdGlvbg0KDQpDZSB0cmF2YWlsIHBvcnRlIHN1ciBsJ2FuYWx5c2UgZGVzIGZsdXggZGUgcGF0aWVudHMgc3VyIHVuIHBsYXRlYXUNCm11dHVhbGlzw6kgZGUgY29uc3VsdGF0aW9ucyBleHRlcm5lcyBkJ3VuIGjDtHBpdGFsLiBMJ29iamVjdGlmIGVzdCBkZQ0KcsOpYWxpc2VyIHVuIGRpYWdub3N0aWMgb2JqZWN0aWYgZGUgbGEgcGVyZm9ybWFuY2Ugb3JnYW5pc2F0aW9ubmVsbGUgZHUNCnNlcnZpY2UgZCd1cm9sb2dpZSBlbiBzJ2FwcHV5YW50IHN1ciBsZXMgbcOpdGhvZGVzIGV0IG91dGlscyBkJ2FuYWx5c2UgZGUNCmZsdXggdnVzIHByw6ljw6lkZW1tZW50IGFpbnNpIHF1ZSBzdXIgbGUgbGFuZ2FnZSBSIMOgIHRyYXZlcnMNCmzigJllbnZpcm9ubmVtZW50IGRlIFJTdHVkaW8uIExhIGZpZ3VyZSBjaS1kZXNzb3VzIGlsbHVzdHJlIGxlcyBwcmluY2lwYXV4DQpmbHV4IGFpbnNpIHF1ZSBsZSBQbGFuIGR1IHBsYXRlYXUgZGUgY29uc3VsdGF0aW9ucyAodm9pciBmaWNoaWVycw0KUGxhbkNvbnN1bHRhdGlvbnMucGRmIGV0IFpvb21VUk8ucGRmIGNvbnN1bHRhYmxlcyDDoCBwYXJ0aXINCjxodHRwOi8vYml0Lmx5L1BsYW5zQ0hVVGxzZT4pDQoNCjwhLS08aW1nIHNyYz0iaWxsdXN0cmF0aW9ucy9QbGFuVXJvLnBuZyIgYWx0PSJQbGFuIGR1IHBsYXRlYXUgZGUgY29uc3VsdGF0aW9ucyIgd2lkdGg9IjYwMCIvPi0tPg0KDQohW1BsYW4gZHUgcGxhdGVhdSBkZQ0KY29uc3VsdGF0aW9uc10oaWxsdXN0cmF0aW9ucy9QbGFuVXJvLnBuZyAiUGxhbiBkdSBwbGF0ZWF1IGRlIGNvbnN1bHRhdGlvbnMiKQ0KQWZpbiBkZSBjb2xsZWN0ZXIgZGVzIGRvbm7DqWVzIHN1ciBsZXMgcGFyY291cnMgc3VpdmlzLCBsZXMgcGF0aWVudHMgcXVpDQpzZSBzb250IHByw6lzZW50w6lzIGxlIDEyLzExLzIwMTUgb250IMOpdMOpIMOpcXVpcMOpcyBk4oCZdW5lIMOpdGlxdWV0dGUNCsOpbGVjdHJvbmlxdWUgKHR5cGUgUkZJRCkgcXVpIGEgcGVybWlzIGRlIHRyYWNlciBsZXVycyBwYXJjb3VycyBkYW5zIGxlDQpwbGF0ZWF1IGRlIGNvbnN1bHRhdGlvbi4gTGVzIGRvbm7DqWVzIGNvbGxlY3TDqWVzIG9udCDDqXTDqSBmdXNpb25uw6llcyBhdmVjDQpsZXMgZG9ubsOpZXMgZGVzIG91dGlscyBkZSBnZXN0aW9uIGRlcyBkb3NzaWVycyBhZG1pbmlzdHJhdGlmcyBldA0KbcOpZGljYXV4IHV0aWxpc8OpcyBwYXIgbGVzIHBlcnNvbm5lbHMuIEwnZW5zZW1ibGUgZXN0IGRpc3BvbmlibGUgc291cyBsYQ0KZm9ybWUgZOKAmXVuIGZpY2hpZXIgbG9nLCBpbGx1c3Ryw6kgcGFyIGxlIHRhYmxlYXUgc3VpdmFudCAodm9pciBhbm5leGUNCkxvZ1BhdGllbnRVUk9zZXVsXzEyMTEyMDE1Lnhsc3ggY29uc3VsdGFibGUgw6AgcGFydGlyDQo8aHR0cDovL2JpdC5seS9sb2dQYXRpZW50cz4pLg0KDQo8IS0tPGltZyBzcmM9ImlsbHVzdHJhdGlvbnMvVGFibGVhdUxvZ1BhdGllbnRzLnBuZyIgYWx0PSJWdWUgZHUgdGFibGVhdSBkZSBkb25uw6llcyBkZSBsb2cgcGF0aWVudCIgd2lkdGg9IjYwMCIvPi0tPg0KDQohWypWdWUgZHUgdGFibGVhdSBkZSBkb25uw6llcyBkZSBsb2cNCnBhdGllbnQqXShpbGx1c3RyYXRpb25zL1RhYmxlYXVMb2dQYXRpZW50cy5wbmcgIlZ1ZSBkdSB0YWJsZWF1IGRlIGRvbm7DqWVzIGRlIGxvZyBwYXRpZW50IikNCg0KTGVzIGRpZmbDqXJlbnRlcyBkZSBjZSB0YWJsZWF1IGRlIGRvbm7DqWVzIHNvbnQgOg0KDQotICAgKklEKiAoQ29sIEEpIDogSWRlbnRpZmlhbnQgZHUgcGF0aWVudA0KLSAgICpUaW1lc3RhbXAgc3RhcnQqIChDb2wgQikgOiBob3JvZGF0YWdlIGVudHLDqWUgZGUgem9uZSBvdSBzYWxsZQ0KLSAgICpUaW1lc3RhbXAgZW5kKiAoQ29sIEMpIDogaG9yb2RhdGFnZSBzb3J0aWUgZGUgem9uZSBvdSBzYWxsZQ0KLSAgICpBY3Rpdml0eV9NQUNSTyogKENvbCBEKSA6IEFjdGl2aXTDqSBzdWl2aWUgZXQgaW5kaWNlIHNhbGxlIChpKQ0KLSAgICpBY3Rpdml0eV9ERVRBSUxTKiAoQ29sIEUpIDogVHlwZSBkJ2FjdGl2aXTDqQ0KLSAgICpSZXNzLkh1bWFpbmVzKiAoQ29sIEYpIDogUmVzc291cmNlcyBodW1haW5lcyBhZG1pbmlzdHJhdGl2ZXMgb3UNCiAgICBzb2lnbmFudGVzIGludGVydmVuYW50IGRhbnMgbCdhY3Rpdml0w6kNCi0gICAqZGlzdGFuY2UgcGFyY291cnVlcyogKENvbCBHKSA6IERpc3RhbmNlIHBhcmNvdXJ1ZSBjdW11bMOpZQ0KLSAgICpkw6lidXQvZmluIG9wWCogKENvbCBIIMOgIE8pIDogaG9yb2RhdGFnZSBkw6lidXQvZmluIGRlIGNoYXF1ZQ0KICAgIG9ww6lyYXRpb24gZGUgcHJpc2UgZW4gY2hhcmdlIHBhciB1bmUgcmVzc291cmNlcyBhZG1pbmlzdHJhdGl2ZSBvdQ0KICAgIHNvaWduYW50ZSAobWF4aSA0IG9ww6lyYXRpb25zIHBhciBhY3Rpdml0w6kpLg0KDQojIyAqKkRldm9pciAyIC0gVHJhdmFpbCBkZW1hbmTDqSoqIDogTW9kw6lsaXNhdGlvbiAqZGF0YS1kcml2ZW4qIGR1IHNlcnZpY2UgZCd1cm9sb2dpZQ0KDQpDZSB0cmF2YWlsIGVzdCDDoCByZW5kcmUgcG91ciBsZSAqKjExLzAyLzIwMjYqKiAoYXZhbnQgbGUgY291cnMpIGF1DQpmb3JtYXQgUm1kIG91IFIgKCtwZGYgc2kgYmVzb2luKSBhdmVjIHBvdXIgdGl0cmUNCiJOT01fUHJlbm9tX0ZJRTUtMi1JT1MtNC1EZXZvaXItMi5cKiINCg0KYGBge3IgaW5jbHVkZT1GQUxTRX0NCmxpYnJhcnkodGlkeXZlcnNlKQ0KbGlicmFyeShkYXRhLnRhYmxlKQ0KbGlicmFyeShjb21wcmVoZW5yKQ0KbGlicmFyeShNQVNTKQ0KYGBgDQoNCkNlIGRldXhpw6htZSBkZXZvaXIgc2UgcGxhY2UgZW4gc3VpdGUgZGlyZWN0ZSBkdSBwcmVtaWVyIGV0IHZvdXMgcHJvcG9zZQ0KdW4gcHJlbWllciB0eXBlIGRlIG1vZMOpbGlzYXRpb24gKmRhdGEtZHJpdmVuKiAoZXQgdG9wIGRvd24pLA0KYydlc3Qtw6AtZGlyZSBiYXPDqSBzdXIgbGVzIHRyYXZhdXggZGUgV2hpdHQgJiBaaGFuZyAoMjAxNykgKkEgZGF0YS1kcml2ZW4NCm1vZGVsIG9mIGFuIGVtZXJnZW5jeSBkZXBhcnRtZW50Ki4NCg0KIyMjIFF1ZXN0aW9uIDEpIExvaSBkZSBMaXR0bGUgKC8zKQ0KDQpQb3VyIHJhcHBlbCwgbGEgbG9pIGRlIExpdHRsZSBlc3QgdW5lIGxvaSBmb25kYW1lbnRhbGUgcXVpLCBkYW5zIHVuDQpjYWRyZSBhc3ltcHRvdGlxdWUsIGxpZSBsZSBuaXZlYXUgZCdvY2N1cGF0aW9uIG1veWVuIGF1IHRlbXBzIGQnYXR0ZW50ZQ0KbW95ZW4gcGFyIGxlIHRhdXggZCdhcnJpdsOpZSBtb3llbiBzZWxvbiBsYSBmb3JtdWxlIGRvbm7DqWUgY2ktZGVzc291cyA6DQokJCBMID0gXGxhbWJkYSBXICQkIC0gJEwkIDogTml2ZWF1IGQnb2NjdXBhdGlvbiBtb3llbiAoYXN5bXB0b3RpcXVlKSAtDQokXGxhbWJkYSQgOiBUYXV4IGQnYXJyaXbDqWUgbW95ZW4gKGFzeW1wdG90aXF1ZSkgLSAkVyQgOiBUZW1wcyBkJ2F0dGVudGUNCm1veWVuIChhc3ltcHRvdGlxdWUpDQoNCkNldHRlIGxvaSBzJ2FwcGxpcXVlIHF1ZWwgcXVlIHNvaXQgbGUgc3lzdMOobWUgb3UgbW9kw6hsZSBjb25zaWTDqXLDqSDDoA0KcGFydGlyIGR1IG1vbWVudCBvw7kgJEw8K1xpbmZ0eSQsICRXPCtcaW5mdHkkIGV0ICRcbGFtYmRhPCtcaW5mdHkkDQooTGl0dGxlIEpEQywgR3JhdmVzIFNDLiAqTGl0dGxl4oCZcyBsYXcqLiBJbjogSW50ZXJuYXRpb25hbCBzZXJpZXMgaW4NCm9wZXJhdGlvbnMgcmVzZWFyY2ggYW5kIG1hbmFnZW1lbnQgc2NpZW5jZS4gMjAwODogODHigJMxMDAuKQ0KDQpQb3VyIGNldHRlIHByZW1pw6hyZSBxdWVzdGlvbiwgaWwgdm91cyBlc3QgZGVtYW5kw6kgZGUgdsOpcmlmaWVyIHF1ZSBjZXR0ZQ0KbG9pICJzJ2FwcGxpcXVlIiBiaWVuIHN1ciBub3RyZSBzZXJ2aWNlIGQndXJvbG9naWUgZHVyYW50IGxhIHDDqXJpb2RlIGRlDQoqKjhoIMOgIDE4aCoqIGRlIGxhIGpvdXJuw6llIGR1IDEyLzExLzIwMTUuDQoNClLDqXBvbnNlIDoNCg0KYGBgICAgICAgICAgDQpBdHRlbnRpb24gOiB1dGlsaXNlciBiaWVuIGxlcyAqKmFycml2w6llcyBpbml0aWFsZXMqKiBkZXMgcGF0aWVudHMgZXQgcGFzIGxlcyBhcnJpdsOpZXMgaW50ZXJtw6lkaWFpcmVzIGFwcsOocyB1bmUgdHJhbnNpdGlvbg0KYGBgDQoNCi0gICBDYWxjdWwgbGUgbml2ZWF1IGQnb2NjdXBhdGlvbiBtb3llbiA6DQoNCiAgICBSYXBwZWwgZXQgY29uc2VpbCA6ICRMID0gXGZyYWN7MX17XGludCBkdH0gXGludCBsKHQpZHQkIGF2ZWMNCiAgICAkbCh0KSA9IEEodCkgLSBEKHQpJCwgdm91cyBwb3V2ZXogY2FsY3VsZXIgZW4gZMOpY29tcG9zYW50IGxhIHDDqXJpb2RlDQogICAgZW4gem9uZSAkaSQgZGUgbcOqbWUgbml2ZWF1IGQnb2NjdXBhdGlvbiBldCBlbiBjYWxjdWxhbnQNCiAgICAkTFs4aDsxOGhdID0gXGZyYWN7MX17XHN1bV9pIHRfaX0gXHN1bV9pIGxfaSB0X2kkIGF2ZWMgJHRfaSQgbGENCiAgICBkdXLDqWUgZGUgbGEgem9uZSAkaSQNCg0KYGBge3J9DQpsaWJyYXJ5KHJlYWR4bCkNCmxpYnJhcnkoZHBseXIpDQoNCiMgQ2FsY3VsIGRlIEwNCiMgTG9hZCB0aGUgRXhjZWwgZGF0YXNldCBQYXRpZW50IFVSTw0KZGYgPC0gcmVhZF9leGNlbCgiTG9nX1BhdGllbnRfVVJPXzEyMTEyMDE1Lnhsc3giKQ0KDQojIFJlbmFtZSBjb2x1bW5zIGZvciBlYXNpZXIgaGFuZGxpbmcNCmRmMSA8LSBkZiAlPiUNCiAgcmVuYW1lKA0KICAgIFJlc3NfSHVtYWluZXMgPSBgUmVzcy4gSHVtYWluZXNgLA0KICAgIFRpbWVzdGFtcF9zdGFydCA9IGBUaW1lc3RhbXAgc3RhcnRgLA0KICAgIFRpbWVzdGFtcF9lbmQgICA9IGBUaW1lc3RhbXAgZW5kYCwNCiAgICBESVNUQU5DRV9QQVJDT1VSVUUgPSBgZGlzdGFuY2UgcGFyY291cnVlYA0KICApICU+JQ0KICBtdXRhdGUoDQogICAgVGltZXN0YW1wX3N0YXJ0ID0gYXMuUE9TSVhjdChUaW1lc3RhbXBfc3RhcnQsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmb3JtYXQgPSAiJWQvJW0vJVkgJUg6JU06JVMiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdHogPSAiRXVyb3BlL1BhcmlzIikNCiAgKQ0KDQojIEdldCB0aW1lIGluIHN5c3RlbSBiYXNlZCBvbiBlbnRyeSBhbmQgZXhpdA0KdGVtcHNfc3lzdGVtZSA8LSBkZjEgJT4lDQogIGdyb3VwX2J5KElEKSAlPiUNCiAgc3VtbWFyaXNlKA0KICAgIGVudHJlZSA9IG1pbihUaW1lc3RhbXBfc3RhcnQsIG5hLnJtID0gVFJVRSksDQogICAgc29ydGllID0gbWF4KFRpbWVzdGFtcF9zdGFydCwgICBuYS5ybSA9IFRSVUUpDQogICkgJT4lDQogIHVuZ3JvdXAoKQ0KDQpldmVudHNfYXJyIDwtIHRlbXBzX3N5c3RlbWUgJT4lDQogIGRwbHlyOjpzZWxlY3QodGltZSA9IGVudHJlZSkgJT4lDQogIG11dGF0ZShkZWx0YSA9IDEpDQoNCmV2ZW50c19kZXAgPC0gdGVtcHNfc3lzdGVtZSAlPiUNCiAgZHBseXI6OnNlbGVjdCh0aW1lID0gc29ydGllKSAlPiUNCiAgbXV0YXRlKGRlbHRhID0gLTEpDQoNCmV2ZW50cyA8LSBiaW5kX3Jvd3MoZXZlbnRzX2FyciwgZXZlbnRzX2RlcCkgJT4lDQogIGFycmFuZ2UodGltZSkNCg0KZXZlbnRzIDwtIGV2ZW50cyAlPiUNCiAgbXV0YXRlKA0KICAgIExfdCA9IGN1bXN1bShkZWx0YSksDQogICAgZHQgPSBhcy5udW1lcmljKGRpZmZ0aW1lKGxlYWQodGltZSksIHRpbWUsIHVuaXRzID0gImhvdXJzIikpDQogICkNCg0KTCA8LSBzdW0oZXZlbnRzJExfdCAqIGV2ZW50cyRkdCwgbmEucm0gPSBUUlVFKSAvDQogICAgIHN1bShldmVudHMkZHQsIG5hLnJtID0gVFJVRSkNCg0KTA0KDQpgYGANCg0KLSAgIENhbGN1bCBkdSB0YXV4IGQnYXJyaXbDqWUgbW95ZW4gOg0KDQpgYGB7cn0NCmxpYnJhcnkoZHBseXIpDQpsaWJyYXJ5KHJlYWR4bCkNCmxpYnJhcnkodGlkeXZlcnNlKQ0KIyBDYWxjdWwgZGUgbGFtYmENCiMgTG9hZCB0aGUgRXhjZWwgZGF0YXNldCBQYXRpZW50IFVSTw0KZGYgPC0gcmVhZF9leGNlbCgiTG9nX1BhdGllbnRfVVJPXzEyMTEyMDE1Lnhsc3giKQ0KZGYNCg0KIyBSZW5hbWUgY29sdW1ucyBmb3IgZWFzaWVyIGhhbmRsaW5nDQpkZjEgPC0gZGYgJT4lDQogICByZW5hbWUoUmVzc19IdW1haW5lcyA9IGBSZXNzLiBIdW1haW5lc2AsDQogICAgICAgIFRpbWVzdGFtcF9zdGFydCA9IGBUaW1lc3RhbXAgc3RhcnRgLA0KICAgICAgICBUaW1lc3RhbXBfZW5kICAgPSBgVGltZXN0YW1wIGVuZGAsDQogICAgICAgIERJU1RBTkNFX1BBUkNPVVJVRSA9IGBkaXN0YW5jZSBwYXJjb3VydWVgKQ0KDQphcnJpdmVlcyA8LSBkZjEgJT4lDQogIGZpbHRlcihBY3Rpdml0eV9NQUNSTyA9PSAiRW50csOpZSBkZXMgQ29uc3VsdGF0aW9ucyIpICU+JQ0KICBncm91cF9ieShJRCkgJT4lDQogIHN1bW1hcmlzZShhcnJpdmVlID0gbWluKFRpbWVzdGFtcF9zdGFydCkpICU+JQ0KICB1bmdyb3VwKCkNCg0KYXJyaXZlZXMNCg0KYXJyaXZlZXNfOF8xOCA8LSBhcnJpdmVlcyAlPiUNCiAgZmlsdGVyKGZvcm1hdChhcnJpdmVlLCAiJUg6JU06JVMiKSA+PSAiMDg6MDA6MDAiLA0KICAgICAgICAgZm9ybWF0KGFycml2ZWUsICIlSDolTTolUyIpIDw9ICIxODowMDowMCIpDQoNCmFycml2ZWVzXzhfMTgNCg0KbGFtYmRhIDwtIG5yb3coYXJyaXZlZXNfOF8xOCkgLyAxMA0KbGFtYmRhDQpgYGANCg0KLSAgIENhbGN1bCBkdSBuaXZlYXUgZCdhdHRlbnRlIG1veWVuIDoNCg0KYGBge3J9DQojIExvYWQgdGhlIEV4Y2VsIGRhdGFzZXQgUGF0aWVudCBVUk8NCmRmIDwtIHJlYWRfZXhjZWwoIkxvZ19QYXRpZW50X1VST18xMjExMjAxNS54bHN4IikNCmRmDQoNCiMgUmVuYW1lIGNvbHVtbnMgZm9yIGVhc2llciBoYW5kbGluZw0KZGYxIDwtIGRmICU+JQ0KICAgcmVuYW1lKFJlc3NfSHVtYWluZXMgPSBgUmVzcy4gSHVtYWluZXNgLA0KICAgICAgICBUaW1lc3RhbXBfc3RhcnQgPSBgVGltZXN0YW1wIHN0YXJ0YCwNCiAgICAgICAgVGltZXN0YW1wX2VuZCAgID0gYFRpbWVzdGFtcCBlbmRgLA0KICAgICAgICBESVNUQU5DRV9QQVJDT1VSVUUgPSBgZGlzdGFuY2UgcGFyY291cnVlYCkNCg0KIyBHZXQgdGltZSBpbiBzeXN0ZW0gYmFzZWQgb24gZW50cnkgYW5kIGV4aXQNCnRlbXBzX3N5c3RlbWUgPC0gZGYxICU+JQ0KICBncm91cF9ieShJRCkgJT4lDQogIHN1bW1hcmlzZSgNCiAgICBlbnRyZWUgPSBtaW4oVGltZXN0YW1wX3N0YXJ0LCBuYS5ybSA9IFRSVUUpLA0KICAgIHNvcnRpZSA9IG1heChUaW1lc3RhbXBfc3RhcnQsIG5hLnJtID0gVFJVRSkNCiAgKSAlPiUNCiAgdW5ncm91cCgpDQoNCnRlbXBzX3N5c3RlbWUNCg0KdGVtcHNfc3lzdGVtZSA8LSB0ZW1wc19zeXN0ZW1lICU+JQ0KICBtdXRhdGUoVyA9IGFzLm51bWVyaWMoZGlmZnRpbWUoc29ydGllLCBlbnRyZWUsIHVuaXRzID0gImhvdXJzIikpKQ0KDQpXIDwtIG1lYW4odGVtcHNfc3lzdGVtZSRXLCBuYS5ybSA9IFRSVUUpDQpXDQpgYGANCg0KLSAgIENvbXBhcmFpc29uIGRlICRMJCBldCAkXGxhbWJkYSBXJCA6DQoNCiAgICBgYGB7cn0NCiAgICBsaWJyYXJ5KGRwbHlyKQ0KICAgIGxpYnJhcnkocmVhZHhsKQ0KDQogICAgIyBPQ0NVUEFUSU9ODQogICAgZXZlbnRzIDwtIGJpbmRfcm93cygNCiAgICAgIHRlbXBzX3N5c3RlbWUgJT4lIHRyYW5zbXV0ZSh0aW1lID0gZW50cmVlLCBkZWx0YSA9IDEpLA0KICAgICAgdGVtcHNfc3lzdGVtZSAlPiUgdHJhbnNtdXRlKHRpbWUgPSBzb3J0aWUsIGRlbHRhID0gLTEpDQogICAgKSAlPiUNCiAgICAgIGFycmFuZ2UodGltZSkgJT4lDQogICAgICBtdXRhdGUoDQogICAgICAgIExfdCA9IGN1bXN1bShkZWx0YSksDQogICAgICAgIGR0ID0gYXMubnVtZXJpYyhkaWZmdGltZShsZWFkKHRpbWUpLCB0aW1lLCB1bml0cyA9ICJob3VycyIpKQ0KICAgICAgKQ0KDQogICAgTCA8LSBzdW0oZXZlbnRzJExfdCAqIGV2ZW50cyRkdCwgbmEucm0gPSBUUlVFKSAvDQogICAgICAgICBzdW0oZXZlbnRzJGR0LCBuYS5ybSA9IFRSVUUpDQoNCiAgICBMIA0KICAgIGxhbWJkYSpXDQogICAgYGBgDQoNCi0gICBSYXBwZWwgZXQgY29uc2VpbCA6IFRlbnRlciBkJ2V4cGxpcXVlciBwb3VycXVvaSBvbiBvYnNlcnZlIHVuZQ0KICAgIGRpZmbDqXJlbmNlLiBFbiBwYXJ0aWN1bGllciwgcG91ciBjZXR0ZSBleHBsaWNhdGlvbiBwZW5zZXIgw6AgcG91cnF1b2kNCiAgICAkXGxhbWJkYSBXJCBwZXV0IMOqdHJlIGFzc2ltaWzDqWUgw6AgdW5lIG1veWVubmUgZGVzIG5pdmVhdXggZGUNCiAgICBwcsOpc2VuY2UgbW95ZW4gZGUgcGF0aWVudHMgJG0kIDoNCiAgICAkXGZyYWN7MX17TX1cc3VtX3ttIFxpbiBNfSBcZnJhY3sxfXt0fVxpbnRfMF50IFxtYXRoYmJ7MX1fbSh0KSBkdCQNCiAgICBhdmVjICRcbWF0aGJiezF9X20odCkkIGxhIGZvbmN0aW9uIGQnaW5kaWNhdHJpY2UgZGUgcHLDqXNlbmNlIGR1DQogICAgcGF0aWVudCAkbSQNCg0KKipSw6lwb25zZSA6KioNCg0KT24gb2JzZXJ2ZSBxdWUgTOKJiDkgYWxvcnMgcXVlIM67XCpX4omIMTIgTGEgZGlmZsOpcmVuY2Ugc+KAmWV4cGxpcXVlIHBhciBsZQ0KZmFpdCBxdWUgOg0KDQotICAgKipFZmZldHMgZGUgYm9yZCB0ZW1wb3JlbHMqKiA6IExhIGpvdXJuw6llIG5lIGNvdXZyZSBwYXMgdW4gY3ljbGUNCiAgICBjb21wbGV0IChwYXRpZW50cyBhcnJpdsOpcyBhdmFudCA4aCBvdSBwYXJ0aXMgYXByw6hzIDE4aCkNCg0KLSAgICoqTm9uLXN0YXRpb25uYXJpdMOpKiogOiBMZXMgYXJyaXbDqWVzIHNvbnQgY29uY2VudHLDqWVzIGxlIG1hdGluLCBsZXMNCiAgICBkw6lwYXJ0cyDDqXRhbMOpcw0KDQotICAgKipNb3llbm5lIHRlbXBvcmVsbGUgdnMgbW95ZW5uZSBwYXIgcGF0aWVudCoqIDogTCBlc3QgdW5lIG1veWVubmUNCiAgICBkYW5zIGxlIHRlbXBzLCDOu1cgZXN0IHVuZSBtb3llbm5lIHN1ciBsZXMgcGF0aWVudHMNCg0KIyMjIFF1ZXN0aW9uIDIpIFByb2Nlc3N1cyBkZSBQb2lzc29uIGQnYXJyaXbDqWUgbm9uIGhvbW9nw6huZSAoLzMpDQoNClVuIHByb2Nlc3N1cyBkZSBwb2lzc29uIGVzdCB1biBwcm9jZXNzdXMgZGUgY29tcHRhZ2UgKGRhbnMgbGUgdGVtcHMpDQppbmRpcXVhbnQgdW4gbm9tYnJlIMOpdsOobmVtZW50cyBheWFudCBvY2N1csOpcyBlbnRyZSB1biB0ZW1wcyAkMCQgZXQgdW4NCnRlbXBzICR0JCBzZWxvbiB1bmUgZGlzdHJpYnV0aW9uIGRlIFBvaXNzb24gJFxtYXRoY2FsKFApKFxsYW1iZGEgKiB0KSQNCmF2ZWMgdW4gdGF1eCBwYXIgdW5pdMOpIGRlIHRlbXBzICRcbGFtYmRhJC4gTm91cyB2ZXJyb25zIGRhbnMgbGUgY291cnMgMw0KcXVlIGxlcyB0ZW1wcyBlbnRyZSBjaGFxdWUgw6l2w6luZW1lbnQgc3VpdmUgdW5lIGxvaSBkZSBkaXN0cmlidXRpb24NCmV4cG9uZW50aWVsbGUgZGUgcGFyYW3DqHRyZSAkXGxhbWJkYSQuDQoNClVuIHByb2Nlc3N1cyBkZSBQb2lzc29uIG5vbiBob21vZ8OobmUgKE5IUFApIGVzdCB1biBwcm9jZXNzdXMgZGUgY29tcHRhZ2UNCm/DuSBsZSB0YXV4IGTigJnDqXbDqW5lbWVudCAkXGxhbWJkYSh0KSQgbidlc3QgcGFzIGNvbnN0YW50LiBFbiBjb25zaWTDqXJhbnQNCnVuZSBtb2TDqWxpc2F0aW9uIGRlIE5IUFAgYmFzw6kgc3VyIHRhdXggZCdhcnJpdsOpZXMgZGVzIHRyYW5jaGVzIFs4aDsxMGhdLA0KXTEwaDsxMmhdLCAuLi4sIF0xNmg7MThoXSBkdSBzZXJ2aWNlIGQndXJvbG9naWUgZ8OpbsOpcmVyIDMwIMOpY2hhbnRpbGxvbnMNCmRlIGNlIE5IUFAgc3VyIGxhIHDDqXJpb2RlIDhoLCAxOGggZXQgaWxsdXN0csOpcyBsZXMuDQoNClBvdXIgdm91cyBhaWRlciB2b2ljaSB1biBleGVtcGxlIHBvdXIgdW4gUHJvY2Vzc3VzIGRlIFBvaXNzb24gaG9tb2fDqG5lIDoNCihuJ2jDqXNpdGV6IHBhcyDDoCBhbGxlciBwbHVzIGxvaW4gYXVzc2kgcG91ciBsJ2lsbHVzdHJhdGlvbikNCg0KYGBge3J9DQpsYW1iZGEgPSAxMCAjIHBhciBoZXVyZQ0Kc2FtcGxlcyA8LSB0aWJibGUocnVuPXRvX3ZlYyhmb3IoaSBpbiAxOjEwKSByZXAoaSwyMDApKSwNCiAgICAgICBpZD1yZXAoMToyMDAsMTApLA0KICAgICAgIGRlbHRhX3Q9cmV4cCgyMDAwLGxhbWJkYSkpICU+JQ0KICBncm91cF9ieShydW4pICU+JQ0KICBtdXRhdGUodCA9IGN1bXN1bShkZWx0YV90KSkgJT4lDQogIGZpbHRlcih0IDw9IDEwKQ0KDQojIGlsbHVzdHJhdGlvbiAxDQpzYW1wbGVzICU+JSANCiAgZ2dwbG90KGFlcyh0LGlkLGNvbG9yPWZhY3RvcihydW4pKSkgKw0KICBnZW9tX3BvaW50KCkgDQoNCiMgaWxsdXN0cmF0aW9uIDINCnNhbXBsZXMgJT4lICAgDQogIG11dGF0ZSh0ID0gY3V0KHQsc2VxKDAsMzAsYnk9MSksaW5jbHVkZS5sb3dlc3QgPSBUUlVFKSkgJT4lDQogIGNvdW50KHJ1bix0KSAlPiUNCiAgYXJyYW5nZShydW4sdCkgJT4lDQogIGdncGxvdChhZXModCxuKSkgKw0KICBnZW9tX2JveHBsb3QoKQ0KYGBgDQoNCmBgYCAgICAgICAgIA0KQXR0ZW50aW9uIDogcG91ciB1biBwcm9jZXNzdXMgZGUgUG9pc3NvbiBub24gaG9tb2fDqG5lLCBpbCBmYXVkcmEgZ8OpbsOpcmVyIGxlIG5vbWJyZSBkJ2Fycml2w6llcyBhdmVjIF9ycG9pc3NfIHBvdXIgY2hhcXVlIHRyYW5jaGUgZGUgdGVtcHMgYXZlYyB1biB0YXV4IGRpZmbDqXJlbnQgZXQgZW5zdWl0ZSBsZXMgcsOpcGFydGlyIHVuaWZvcm3DqW1lbnQgZGFucyBsZSB0ZW1wcyAoYXUgc2VpbiBkZSBsZXVyIGludGVydmFsbGUpIGVuIGfDqW7DqXJhbnQgZGVzIHZhbHVlcnMgdW5pZm9ybSAoX3J1bmlmXykuDQpgYGANCg0KUsOpcG9uc2UgOg0KDQpgYGB7ciBhbnN3ZXIgcTJ9DQoNCiMgSW1wb3J0IGRlcyBkb25uw6llcw0KZGF0YV9yYXcgPC0gcmVhZF9leGNlbCgiTG9nX1BhdGllbnRfVVJPXzEyMTEyMDE1Lnhsc3giKQ0KDQojIG9uIGdhcmRlIHF1ZSBsJ2Fycml2w6llIGluaXRpYWxlDQpkYXRhX2Fycml2YWxzIDwtIGRhdGFfcmF3ICU+JQ0KICBtdXRhdGUoYXJyaXZhbF90aW1lID0geW1kX2htcyhgVGltZXN0YW1wIHN0YXJ0YCkpICU+JQ0KICBncm91cF9ieShJRCkgJT4lICAgICAgICAgICAgICAgIA0KICBzbGljZV9taW4oYXJyaXZhbF90aW1lLCBuID0gMSkgJT4lICAgICAgICANCiAgdW5ncm91cCgpICU+JQ0KICBtdXRhdGUoDQogICAgaG91ciA9IGhvdXIoYXJyaXZhbF90aW1lKSArIG1pbnV0ZShhcnJpdmFsX3RpbWUpLzYwDQogICkgJT4lDQogIGZpbHRlcihob3VyID49IDgsIGhvdXIgPCAxOCkNCg0KIyBEw6ljb3VwYWdlIGVuIHRyYW5jaGVzIGhvcmFpcmVzDQpkYXRhX2Fycml2YWxzIDwtIGRhdGFfYXJyaXZhbHMgJT4lDQogIG11dGF0ZSgNCiAgICB0cmFuY2hlID0gY3V0KA0KICAgICAgaG91ciwNCiAgICAgIGJyZWFrcyA9IGMoOCwxMCwxMiwxNCwxNiwxOCksDQogICAgICBpbmNsdWRlLmxvd2VzdCA9IFRSVUUsDQogICAgICByaWdodCA9IFRSVUUNCiAgICApDQogICkNCg0KIyBFc3RpbWF0aW9uIGRlcyB0YXV4IM67KHQpDQpsYW1iZGFfaGF0IDwtIGRhdGFfYXJyaXZhbHMgJT4lDQogIGNvdW50KHRyYW5jaGUpICU+JQ0KICBtdXRhdGUobGFtYmRhID0gbiAvIDIpIA0KDQojIETDqWZpbml0aW9uIGRlcyBpbnRlcnZhbGxlcw0KaW50ZXJ2YWxzIDwtIHRpYmJsZSgNCiAgc3RhcnQgID0gYyg4LDEwLDEyLDE0LDE2KSwNCiAgZW5kICAgID0gYygxMCwxMiwxNCwxNiwxOCksDQogIGxhbWJkYSA9IGxhbWJkYV9oYXQkbGFtYmRhDQopDQoNCiMgU2ltdWxhdGlvbiBkdSBOSFBQICgzMCBydW5zKQ0Kc2V0LnNlZWQoMTIzKQ0Kbl9ydW5zIDwtIDMwDQoNCnNhbXBsZXNfbmhwcCA8LSBtYXBfZGZyKDE6bl9ydW5zLCBmdW5jdGlvbihydW5faWQpIHsNCg0KICBtYXBfZGZyKDE6bnJvdyhpbnRlcnZhbHMpLCBmdW5jdGlvbihpKSB7DQoNCiAgICBkdCA8LSBpbnRlcnZhbHMkZW5kW2ldIC0gaW50ZXJ2YWxzJHN0YXJ0W2ldDQogICAgbl9ldmVudHMgPC0gcnBvaXMoMSwgaW50ZXJ2YWxzJGxhbWJkYVtpXSAqIGR0KQ0KDQogICAgdGliYmxlKA0KICAgICAgcnVuID0gcnVuX2lkLA0KICAgICAgdCA9IHJ1bmlmKG5fZXZlbnRzLCBpbnRlcnZhbHMkc3RhcnRbaV0sIGludGVydmFscyRlbmRbaV0pDQogICAgKQ0KICB9KQ0KfSkgJT4lDQogIGFycmFuZ2UocnVuLCB0KSAlPiUNCiAgZ3JvdXBfYnkocnVuKSAlPiUNCiAgbXV0YXRlKGlkID0gcm93X251bWJlcigpKSAlPiUNCiAgdW5ncm91cCgpDQoNCiMgSWxsdXN0cmF0aW9uIDEgOiB0cmFqZWN0b2lyZXMNCnNhbXBsZXNfbmhwcCAlPiUNCiAgZ2dwbG90KGFlcyh4ID0gdCwgY29sb3IgPSBmYWN0b3IocnVuKSkpICsNCiAgc3RhdF9lY2RmKGdlb20gPSAic3RlcCIsIGFscGhhID0gMC41KSArDQogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBzY2FsZXM6OnBlcmNlbnQpICsNCiAgbGFicygNCiAgICB0aXRsZSA9ICJUcmFqZWN0b2lyZXMgZHUgTkhQUCDigJMgRm9uY3Rpb24gZGUgY29tcHRhZ2UiLA0KICAgIHggPSAiVGVtcHMgKGhldXJlcykiLA0KICAgIHkgPSAiUHJvcG9ydGlvbiBkJ2Fycml2w6llcyBjdW11bMOpZXMiDQogICkgKw0KICB0aGVtZV9taW5pbWFsKCkgKw0KICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpDQoNCiMgQ2FsY3VsIGRlcyBhcnJpdsOpZXMgcsOpZWxsZXMgcGFyIGhldXJlDQpyZWFsX2Fycml2YWxzIDwtIGRhdGFfYXJyaXZhbHMgJT4lDQogIG11dGF0ZShob3VyID0gY3V0KGhvdXIsIHNlcSg4LDE4LGJ5PTEpLCBpbmNsdWRlLmxvd2VzdCA9IFRSVUUpKSAlPiUNCiAgY291bnQoaG91cikNCg0KIyBJbGx1c3RyYXRpb24gMiBBTcOJTElPUsOJRQ0Kc2FtcGxlc19uaHBwICU+JQ0KICBtdXRhdGUoaG91ciA9IGN1dCh0LCBzZXEoOCwxOCxieT0xKSwgaW5jbHVkZS5sb3dlc3QgPSBUUlVFKSkgJT4lDQogIGNvdW50KHJ1biwgaG91cikgJT4lDQogIGdncGxvdChhZXMoaG91ciwgbikpICsNCiAgZ2VvbV9ib3hwbG90KGZpbGwgPSAibGlnaHRibHVlIiwgYWxwaGEgPSAwLjYpICsNCiAgZ2VvbV9wb2ludChkYXRhID0gcmVhbF9hcnJpdmFscywgYWVzKGhvdXIsIG4pLCANCiAgICAgICAgICAgICBjb2xvciA9ICJyZWQiLCBzaXplID0gMywgc2hhcGUgPSAxOCkgKyAgDQogIGxhYnMoDQogICAgdGl0bGUgPSAiRGlzdHJpYnV0aW9uIGRlcyBhcnJpdsOpZXMgcGFyIGhldXJlIiwNCiAgICBzdWJ0aXRsZSA9ICJCb3hwbG90OiBOSFBQIHNpbXVsw6kgKDMwIHJ1bnMpIHwgTG9zYW5nZXMgcm91Z2VzOiBkb25uw6llcyByw6llbGxlcyIsDQogICAgeCA9ICJIZXVyZSIsDQogICAgeSA9ICJOb21icmUgZCdhcnJpdsOpZXMiDQogICkgKw0KICB0aGVtZV9taW5pbWFsKCkNCmBgYA0KDQojIyMjIEludGVycHLDqXRhdGlvbiBkZXMgcsOpc3VsdGF0cyA6DQoNCkNlIGdyYXBoaXF1ZSByZXByw6lzZW50ZSBwbHVzaWV1cnMgdHJhamVjdG9pcmVzIHNpbXVsw6llcyBkZSBsYSBmb25jdGlvbg0KZGUgY29tcHRhZ2UgY3VtdWzDqWUgZOKAmXVuIHByb2Nlc3N1cyBkZSBQb2lzc29uIG5vbiBob21vZ8OobmUgKE5IUFApIGF1DQpjb3VycyBkdSB0ZW1wcywgZXhwcmltw6kgZW4gaGV1cmVzLiBDaGFxdWUgY291cmJlIGNvbG9yw6llIGNvcnJlc3BvbmQgw6ANCnVuZSByw6lhbGlzYXRpb24gcG9zc2libGUgZGVzIGFycml2w6llcywgY2UgcXVpIGV4cGxpcXVlIGxlcyDDqWNhcnRzDQpvYnNlcnbDqXMgZW50cmUgZWxsZXMgZXQgdHJhZHVpdCBsZSBjYXJhY3TDqHJlIGFsw6lhdG9pcmUgZHUgcHJvY2Vzc3VzLg0KVG91dGVzIGxlcyB0cmFqZWN0b2lyZXMgc29udCBjcm9pc3NhbnRlcywgcGFzc2FudCBwcm9ncmVzc2l2ZW1lbnQgZGUgMCAlDQrDoCAxMDAgJSBk4oCZYXJyaXbDqWVzLCBwdWlzcXVlIGxlcyDDqXbDqW5lbWVudHMgc+KAmWFjY3VtdWxlbnQgc2FucyBqYW1haXMNCmRpbWludWVyLiBMYSBwZW50ZSBkZXMgY291cmJlcyB2YXJpZSBzZWxvbiBsZXMgcMOpcmlvZGVzIDogZWxsZSBlc3QgcGx1cw0KZmFpYmxlIGF1IGTDqWJ1dCBkZSBs4oCZaW50ZXJ2YWxsZSB0ZW1wb3JlbCwgc+KAmWFjY2VudHVlIG5ldHRlbWVudCBhdQ0KbWlsaWV1LCBpbmRpcXVhbnQgdW5lIGludGVuc2l0w6kgZOKAmWFycml2w6llIHBsdXMgw6lsZXbDqWUsIHB1aXMgcmFsZW50aXQgZW4NCmZpbiBkZSBww6lyaW9kZSBsb3JzcXVlIGxhIHRvdGFsaXTDqSBkZXMgYXJyaXbDqWVzIGVzdCBwcmVzcXVlIGF0dGVpbnRlLg0KTOKAmWVuc2VtYmxlIG1ldCBhaW5zaSBlbiDDqXZpZGVuY2UgdW5lIGludGVuc2l0w6kgZMOpcGVuZGFudGUgZHUgdGVtcHMsDQpjYXJhY3TDqXJpc3RpcXVlIHByaW5jaXBhbGUgZOKAmXVuIHByb2Nlc3N1cyBkZSBQb2lzc29uIG5vbiBob21vZ8OobmUsIHRvdXQNCmVuIG1vbnRyYW50IGxhIHZhcmlhYmlsaXTDqSBuYXR1cmVsbGUgZW50cmUgZGlmZsOpcmVudGVzIHLDqWFsaXNhdGlvbnMNCmF1dG91ciBk4oCZdW5lIHRlbmRhbmNlIG1veWVubmUgY29tbXVuZS4NCg0KTGUgc2Vjb25kIGdyYXBoaXF1ZSBtb250cmUgbGEgZGlzdHJpYnV0aW9uIGR1IG5vbWJyZSBk4oCZYXJyaXbDqWVzIHBhcg0KdHJhbmNoZSBob3JhaXJlLCBlbiBjb21wYXJhbnQgdW4gbW9kw6hsZSBzaW11bMOpIE5IUFAgYXV4IGRvbm7DqWVzIHLDqWVsbGVzLg0KQ2hhcXVlIGJveHBsb3QgcsOpc3VtZSBsYSB2YXJpYWJpbGl0w6kgZGVzIGFycml2w6llcyBzaW11bMOpZXMgcG91ciB1bmUNCmhldXJlIGRvbm7DqWUgKG3DqWRpYW5lLCBkaXNwZXJzaW9uLCB2YWxldXJzIGV4dHLDqm1lcyksIHRhbmRpcyBxdWUgbGUNCmxvc2FuZ2UgaW5kaXF1ZSBsYSB2YWxldXIgb2JzZXJ2w6llLiBPbiB2b2l0IHVuZSBhdWdtZW50YXRpb24gcHJvZ3Jlc3NpdmUNCmRlcyBhcnJpdsOpZXMgZW4gbWF0aW7DqWUgZW50cmUgOGggZXQgMTJoLCB1biBjcmV1eCBtYXJxdcOpIGF1dG91ciBkZQ0KMTJo4oCTMTRoLCBwdWlzIHVuZSByZXByaXNlIG5ldHRlIGVuIG1pbGlldSBk4oCZYXByw6hzLW1pZGkgKDE0aOKAkzE2aCksIGF2YW50DQp1bmUgZGltaW51dGlvbiBlbiBmaW4gZGUgam91cm7DqWUuIEdsb2JhbGVtZW50LCBsZXMgbG9zYW5nZXMgcm91Z2VzIHNlDQpzaXR1ZW50IHNvdXZlbnQgw6AgbOKAmWludMOpcmlldXIgb3UgcHJvY2hlcyBkZXMgYm94cGxvdHMsIGNlIHF1aSBzdWdnw6hyZQ0KcXVlIGxlIG1vZMOobGUgcmVwcm9kdWl0IGNvcnJlY3RlbWVudCBsZSBuaXZlYXUgZXQgbGEgdmFyaWFiaWxpdMOpIGRlcw0KYXJyaXbDqWVzIHNlbG9uIGzigJloZXVyZSwgbcOqbWUgc2kgcXVlbHF1ZXMgw6ljYXJ0cyBhcHBhcmFpc3NlbnQsIG5vdGFtbWVudA0Kc3VyIGxhIHRyYW5jaGUgaG9yYWlyZSBkZSAxMGgtMTFoIG/DuSBsYSBkb25uw6kgcsOpZWxsZSBlc3QgYmllbiBwbHVzIGhhdXRlDQpxdWUgY2VsbGVzIHNpbXVsw6llcy4NCg0KRW4gY29uY2x1c2lvbiwgbOKAmWFuYWx5c2UgY29uam9pbnRlIGRlcyB0cmFqZWN0b2lyZXMgY3VtdWzDqWVzIGV0IGRlcw0KZGlzdHJpYnV0aW9ucyBob3JhaXJlcyBtb250cmUgcXVlIGxlIHByb2Nlc3N1cyBkZSBQb2lzc29uIG5vbiBob21vZ8OobmUNCmNvbnN0aXR1ZSB1bmUgbW9kw6lsaXNhdGlvbiBnbG9iYWxlbWVudCBwZXJ0aW5lbnRlIGRlcyBhcnJpdsOpZXMNCm9ic2VydsOpZXMuIExlIE5IUFAgY2FwdHVyZSBjb3JyZWN0ZW1lbnQgbGEgZMOpcGVuZGFuY2UgdGVtcG9yZWxsZSBkZQ0KbOKAmWludGVuc2l0w6ksIGVuIHJlcHJvZHVpc2FudCDDoCBsYSBmb2lzIGxhIGR5bmFtaXF1ZSBnbG9iYWxlIGRlcyBhcnJpdsOpZXMNCmV0IGxlcyB2YXJpYXRpb25zIG1hcnF1w6llcyBzZWxvbiBsZXMgdHJhbmNoZXMgaG9yYWlyZXMsIG5vdGFtbWVudCBsZQ0KY3JldXggZGUgbGEgbWktam91cm7DqWUgZXQgbGUgcGljIGRlIGzigJlhcHLDqHMtbWlkaS4gTGEgcHJveGltaXTDqSBkZXMNCmRvbm7DqWVzIHLDqWVsbGVzIGF2ZWMgbGVzIGRpc3RyaWJ1dGlvbnMgc2ltdWzDqWVzIGNvbmZpcm1lIGxhIGNhcGFjaXTDqSBkdQ0KbW9kw6hsZSDDoCByZW5kcmUgY29tcHRlIGRlIGxhIHZhcmlhYmlsaXTDqSBuYXR1cmVsbGUgZHUgcGjDqW5vbcOobmUuDQpOw6lhbm1vaW5zLCBjZXJ0YWlucyDDqWNhcnRzLCBjb21tZSBsYSBzb3VzLWVzdGltYXRpb24gZGVzIGFycml2w6llcyBzdXIgbGENCnRyYW5jaGUgMTBo4oCTMTFoLCBzdWdnw6hyZW50IHF1ZSBs4oCZaW50ZW5zaXTDqSBwb3VycmFpdCDDqnRyZSBhZmZpbsOpZQ0KbG9jYWxlbWVudCBhZmluIGTigJlhbcOpbGlvcmVyIGzigJlhZMOpcXVhdGlvbiBkdSBtb2TDqGxlLg0KDQojIyMgUXVlc3Rpb24gMykgTW9kw6lsaXNhdGlvbiBkZSBsYSBkdXLDqWUgZGUgc8Opam91ciBwYXIgdW5lIGRpc3RyaWJ1dGlvbiAoLzMpDQoNCkEgbCdhaWRlIGRlIGxhIGRvY3VtZW50YXRpb24gIlJpY2NpLWRpc3RyaWJ1dGlvbnMtZW4ucGRmIiBmb3VybmllLA0Kbm90YW1tZW50IGF2ZWMgbGEgZm9uY3Rpb24gZml0ZGlzdHIoKSBkZSBsYSBsaWJyYWlyaWUgTUFTUywgdGVzdGVyIGxhDQptb2TDqWxpc2F0aW9uIChwYXIgTUxFLCAqTWF4aW11bSBMaWtlbGlob29kIEVzdGltYXRpb24qKSBsYSBkdXLDqWUgZGUNCnPDqWpvdXJzIGRlIGwnZW5zZW1ibGUgZGVzIHBhdGllbnRzIGF2ZWMgZGlmZsOpcmVudGVzIGRpc3RyaWJ1dGlvbnMgOg0Kbm9ybWFsZSwgZXhwb25lbnRpZWxsZSwgZ2FtbWEsIHdlaWJ1bGwuDQoNClLDqXBvbnNlIDoNCg0KKkVudHJleiB2b3RyZSB0ZXh0ZSBpY2kqDQoNCmBgYHtyfQ0KI19FbnRyZXogdm90cmUgY29kZSBSIGljaV8NCg0KbGlicmFyeShyZWFkeGwpDQpsaWJyYXJ5KE1BU1MpDQoNCmRhdGFfc2VydmljZSA8LSByZWFkX2V4Y2VsKCJMb2dfUGF0aWVudF9VUk9fMTIxMTIwMTUueGxzeCIpDQoNCmRhdGFfcmF3IDwtIGRhdGFfc2VydmljZSAlPiUNCiAgbXV0YXRlKA0KICAgIGRhdGV0aW1lX2JlZ2luID0geW1kX2htcyhgVGltZXN0YW1wIHN0YXJ0YCksDQogICAgZGF0ZXRpbWVfZW5kICAgPSB5bWRfaG1zKGBUaW1lc3RhbXAgZW5kYCkNCiAgKQ0KDQpkYXRhX3BhdGllbnQgPC0gZGF0YV9yYXcgJT4lDQogIGdyb3VwX2J5KElEKSAlPiUNCiAgc3VtbWFyaXNlKA0KICAgIGFycml2YWxfdGltZSA9IG1pbihkYXRldGltZV9iZWdpbiwgbmEucm0gPSBUUlVFKSwNCiAgICBkZXBhcnR1cmVfdGltZSA9IG1heChkYXRldGltZV9lbmQsIG5hLnJtID0gVFJVRSksDQogICAgLmdyb3VwcyA9ICJkcm9wIg0KICApDQoNCmRhdGFfcGF0aWVudCA8LSBkYXRhX3BhdGllbnQgJT4lDQogIG11dGF0ZSgNCiAgICBkdXJlZV9zZWpvdXIgPSBhcy5udW1lcmljKGRpZmZ0aW1lKGRlcGFydHVyZV90aW1lLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYXJyaXZhbF90aW1lLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdW5pdHMgPSAiaG91cnMiKSkNCiAgKQ0KDQpkdXJlZSA8LSBkYXRhX3BhdGllbnQkZHVyZWVfc2Vqb3VyDQpkdXJlZSA8LSBkdXJlZVshaXMubmEoZHVyZWUpXQ0KZHVyZWUgPC0gZHVyZWVbZHVyZWUgPiAwXQ0KDQpzdW1tYXJ5KGR1cmVlKQ0KaGlzdChkdXJlZSwgYnJlYWtzID0gMzApDQoNCmZpdF9ub3JtIDwtIGZpdGRpc3RyKGR1cmVlLCAibm9ybWFsIikNCmZpdF9ub3JtDQoNCmZpdF9leHAgPC0gZml0ZGlzdHIoZHVyZWUsICJleHBvbmVudGlhbCIpDQpmaXRfZXhwDQoNCmZpdF9nYW1tYSA8LSBmaXRkaXN0cihkdXJlZSwgImdhbW1hIikNCmZpdF9nYW1tYQ0KDQpmaXRfd2VpYiA8LSBmaXRkaXN0cihkdXJlZSwgIndlaWJ1bGwiKQ0KZml0X3dlaWINCg0KQUlDX2ZpdGRpc3RyIDwtIGZ1bmN0aW9uKGZpdCkgew0KICBrIDwtIGxlbmd0aChmaXQkZXN0aW1hdGUpDQogIC0yICogZml0JGxvZ2xpayArIDIgKiBrDQp9DQoNCkFJQ19ub3JtICA8LSBBSUNfZml0ZGlzdHIoZml0X25vcm0pDQpBSUNfZXhwICAgPC0gQUlDX2ZpdGRpc3RyKGZpdF9leHApDQpBSUNfZ2FtbWEgPC0gQUlDX2ZpdGRpc3RyKGZpdF9nYW1tYSkNCkFJQ193ZWliICA8LSBBSUNfZml0ZGlzdHIoZml0X3dlaWIpDQoNCkFJQ192YWx1ZXMgPC0gZGF0YS5mcmFtZSgNCiAgRGlzdHJpYnV0aW9uID0gYygiTm9ybWFsZSIsICJFeHBvbmVudGllbGxlIiwgIkdhbW1hIiwgIldlaWJ1bGwiKSwNCiAgQUlDID0gYyhBSUNfbm9ybSwgQUlDX2V4cCwgQUlDX2dhbW1hLCBBSUNfd2VpYikNCikNCg0KQUlDX3ZhbHVlc1tvcmRlcihBSUNfdmFsdWVzJEFJQyksIF0NCg0KaGlzdChkdXJlZSwgcHJvYiA9IFRSVUUsIGJyZWFrcyA9IDMwLA0KICAgICBtYWluID0gIkFqdXN0ZW1lbnQgZGVzIGxvaXMg4oCTIER1csOpZSBkZSBzw6lqb3VyIiwNCiAgICAgeGxhYiA9ICJEdXLDqWUiKQ0KDQpjdXJ2ZShkbm9ybSh4LA0KICAgICAgICAgICAgbWVhbiA9IGZpdF9ub3JtJGVzdGltYXRlWzFdLA0KICAgICAgICAgICAgc2QgICA9IGZpdF9ub3JtJGVzdGltYXRlWzJdKSwNCiAgICAgIGFkZCA9IFRSVUUsIGNvbCA9ICJibHVlIiwgbHdkID0gMikNCg0KY3VydmUoZGV4cCh4LA0KICAgICAgICAgICByYXRlID0gZml0X2V4cCRlc3RpbWF0ZSksDQogICAgICBhZGQgPSBUUlVFLCBjb2wgPSAicmVkIiwgbHdkID0gMikNCg0KY3VydmUoZGdhbW1hKHgsDQogICAgICAgICAgICAgc2hhcGUgPSBmaXRfZ2FtbWEkZXN0aW1hdGVbInNoYXBlIl0sDQogICAgICAgICAgICAgcmF0ZSAgPSBmaXRfZ2FtbWEkZXN0aW1hdGVbInJhdGUiXSksDQogICAgICBhZGQgPSBUUlVFLCBjb2wgPSAiZ3JlZW4iLCBsd2QgPSAyKQ0KDQpjdXJ2ZShkd2VpYnVsbCh4LA0KICAgICAgICAgICAgICAgc2hhcGUgPSBmaXRfd2VpYiRlc3RpbWF0ZVsic2hhcGUiXSwNCiAgICAgICAgICAgICAgIHNjYWxlID0gZml0X3dlaWIkZXN0aW1hdGVbInNjYWxlIl0pLA0KICAgICAgYWRkID0gVFJVRSwgY29sID0gInB1cnBsZSIsIGx3ZCA9IDIpDQoNCmxlZ2VuZCgidG9wcmlnaHQiLA0KICAgICAgIGxlZ2VuZCA9IGMoIk5vcm1hbGUiLCAiRXhwb25lbnRpZWxsZSIsICJHYW1tYSIsICJXZWlidWxsIiksDQogICAgICAgY29sID0gYygiYmx1ZSIsICJyZWQiLCAiZ3JlZW4iLCAicHVycGxlIiksDQogICAgICAgbHdkID0gMikNCg0KYGBgDQoNClF1ZWxsZXMgZXN0IGxhIG1laWxsZXVyZSBkaXN0cmlidXRpb24gPyAoSnVzdGlmaWV6KSBDb21wYXJleiBhdXNzaSBsZXMNCm1veWVubmVzLCDDqWNhcnQtdHlwZXMgZXQgY29lZmZpY2llbnRzIGRlIHZhcmlhdGlvbiBvYnRlbnVzIHBvdXIgY2hhcXVlDQpkaXN0cmlidXRpb24gZXQgcGFyIHJhcHBvcnQgYXUgY2FsY3VsIGRpcmVjdCBkZXMgaW5kaWNhdGV1cnMgc3VyIGxlcw0KdmFyaWFibGVzIHN0YXRpc3RpcXVlcy4NCg0KUsOpcG9uc2UgOg0KDQpQYXJtaSBsZXMgZGlzdHJpYnV0aW9ucyDDqXR1ZGnDqWVzLCBsYSBsb2kgZGUgV2VpYnVsbCBhcHBhcmHDrnQgY29tbWUgbGENCnBsdXMgYXBwcm9wcmnDqWUgcG91ciBtb2TDqWxpc2VyIGxhIGR1csOpZSBkZSBzw6lqb3VyLiBFbGxlIHJlc3BlY3RlIGxlDQpzdXBwb3J0IHN0cmljdGVtZW50IHBvc2l0aWYgZGUgbGEgdmFyaWFibGUgZXQgcmVwcm9kdWl0IGNvcnJlY3RlbWVudCBsZXMNCnByaW5jaXBhdXggaW5kaWNhdGV1cnMgc3RhdGlzdGlxdWVzLCBlbiBwYXJ0aWN1bGllciBsYSB2YXJpYWJpbGl0w6kNCm9ic2VydsOpZS4gTGEgbG9pIEdhbW1hIGF1cmFpdCDDqWdhbGVtZW50IHB1IGNvbnN0aXR1ZXIgdW4gY2hvaXgNCnBlcnRpbmVudCwgY2FyIGVsbGUgcHLDqXNlbnRlIGRlcyBjYXJhY3TDqXJpc3RpcXVlcyBwcm9jaGVzIGV0IHVuIGJvbg0KYWNjb3JkIGF2ZWMgbGVzIGRvbm7DqWVzIGVtcGlyaXF1ZXMgOyB0b3V0ZWZvaXMsIGxhIFdlaWJ1bGwgb2ZmcmUgdW5lDQpmbGV4aWJpbGl0w6kgbMOpZ8OocmVtZW50IHN1cMOpcmlldXJlLCBub3RhbW1lbnQgZGFucyBsYSBtb2TDqWxpc2F0aW9uIGRlcw0KZHVyw6llcyBleHRyw6ptZXMgZXQgZGFucyBs4oCZaW50ZXJwcsOpdGF0aW9uIGR1IGNvbXBvcnRlbWVudCBkdSB0YXV4IGRlDQpzb3J0aWUgYXUgY291cnMgZHUgc8Opam91ci4gTGEgbG9pIG5vcm1hbGUsIGJpZW4gcXVlIG51bcOpcmlxdWVtZW50DQpwcm9jaGUsIHJlc3RlIGNvbmNlcHR1ZWxsZW1lbnQgaW5hZGFwdMOpZSBlbiByYWlzb24gZGUgc29uIHN1cHBvcnQgbm9uDQpib3Juw6kgaW5mw6lyaWV1ciwgdGFuZGlzIHF1ZSBsYSBsb2kgZXhwb25lbnRpZWxsZSBlc3QgY2xhaXJlbWVudA0KaW5hcHByb3ByacOpZSBjYXIgZWxsZSBpbXBvc2UgdW5lIHZhcmlhYmlsaXTDqSB0cm9wIMOpbGV2w6llIHBhciByYXBwb3J0IGF1eA0Kb2JzZXJ2YXRpb25zLiBBaW5zaSwgbGEgbG9pIGRlIFdlaWJ1bGwgZXN0IHJldGVudWUgY29tbWUgbWVpbGxldXINCmNvbXByb21pcywgbGEgbG9pIEdhbW1hIHBvdXZhbnQgw6p0cmUgY29uc2lkw6lyw6llIGNvbW1lIHVuZSBhbHRlcm5hdGl2ZQ0KdmFsYWJsZSBlbiBzZWNvbmQgY2hvaXguDQoNCmBgYHtyfQ0KI19FbnRyZXogdm90cmUgY29kZSBSIGljaV8NCg0KIyBJbmRpY2F0ZXVycyBlbXBpcmlxdWVzDQptZWFuX2VtcCA8LSBtZWFuKGR1cmVlKQ0Kc2RfZW1wICAgPC0gc2QoZHVyZWUpDQpjdl9lbXAgICA8LSBzZF9lbXAgLyBtZWFuX2VtcA0KDQptZWFuX25vcm0gPC0gZml0X25vcm0kZXN0aW1hdGVbIm1lYW4iXQ0Kc2Rfbm9ybSAgIDwtIGZpdF9ub3JtJGVzdGltYXRlWyJzZCJdDQpjdl9ub3JtICAgPC0gc2Rfbm9ybSAvIG1lYW5fbm9ybQ0KDQpyYXRlX2V4cCA8LSBmaXRfZXhwJGVzdGltYXRlWyJyYXRlIl0NCg0KbWVhbl9leHAgPC0gMSAvIHJhdGVfZXhwDQpzZF9leHAgICA8LSAxIC8gcmF0ZV9leHANCmN2X2V4cCAgIDwtIHNkX2V4cCAvIG1lYW5fZXhwDQoNCnNoYXBlX2cgPC0gZml0X2dhbW1hJGVzdGltYXRlWyJzaGFwZSJdDQpyYXRlX2cgIDwtIGZpdF9nYW1tYSRlc3RpbWF0ZVsicmF0ZSJdDQoNCm1lYW5fZ2FtbWEgPC0gc2hhcGVfZyAvIHJhdGVfZw0Kc2RfZ2FtbWEgICA8LSBzcXJ0KHNoYXBlX2cpIC8gcmF0ZV9nDQpjdl9nYW1tYSAgIDwtIHNkX2dhbW1hIC8gbWVhbl9nYW1tYQ0KDQpzaGFwZV93IDwtIGZpdF93ZWliJGVzdGltYXRlWyJzaGFwZSJdDQpzY2FsZV93IDwtIGZpdF93ZWliJGVzdGltYXRlWyJzY2FsZSJdDQoNCm1lYW5fd2VpYiA8LSBzY2FsZV93ICogZ2FtbWEoMSArIDEgLyBzaGFwZV93KQ0Kc2Rfd2VpYiAgIDwtIHNjYWxlX3cgKiBzcXJ0KA0KICBnYW1tYSgxICsgMiAvIHNoYXBlX3cpIC0gZ2FtbWEoMSArIDEgLyBzaGFwZV93KV4yDQopDQpjdl93ZWliIDwtIHNkX3dlaWIgLyBtZWFuX3dlaWINCg0KY29tcGFyYWlzb25fdGFibGUgPC0gZGF0YS5mcmFtZSgNCiAgRGlzdHJpYnV0aW9uID0gYygiRW1waXJpcXVlIiwgIk5vcm1hbGUiLCAiRXhwb25lbnRpZWxsZSIsICJHYW1tYSIsICJXZWlidWxsIiksDQogIE1veWVubmUgPSBjKG1lYW5fZW1wLCBtZWFuX25vcm0sIG1lYW5fZXhwLCBtZWFuX2dhbW1hLCBtZWFuX3dlaWIpLA0KICBFY2FydF90eXBlID0gYyhzZF9lbXAsIHNkX25vcm0sIHNkX2V4cCwgc2RfZ2FtbWEsIHNkX3dlaWIpLA0KICBDb2VmZmljaWVudF92YXJpYXRpb24gPSBjKGN2X2VtcCwgY3Zfbm9ybSwgY3ZfZXhwLCBjdl9nYW1tYSwgY3Zfd2VpYikNCikNCg0KY29tcGFyYWlzb25fdGFibGUNCg0KYGBgDQoNClF1ZWwgcXVlIHNvaXQgdm90cmUgcsOpcG9uc2UsIHJlZmFpdGVzIGNlIHRyYXZhaWwgcG91ciBsYSBkaXN0cmlidXRpb24NCmdhbW1hIGVuIHPDqXBhcmFudCBsYSBtb2TDqWxpc2F0aW9uIGRlcyBkdXLDqWVzIGRlIHPDqWpvdXIgZGVzIHBhdGllbnRzDQpwcmlvcml0YWlyZXMgZXQgbm9uIHByaW9yaXRhaXJlcyBldCBjb21wYXJleiBsZXMgZGV1eCBkaXN0cmlidXRpb25zDQpncmFwaGlxdWVtZW50IGV0IMOgIGwnYWlkZSBsZXVyIG1vbWVudHMgKGVzcMOpcmFuY2UsIHZhcmlhbmNlLCBjb2VmZmljaWVudA0KZGUgdmFyaWF0aW9uKSBldC9vdSBsZXVyIHBhcmFtw6h0cmVzIGQnw6ljaGVsbGUgZXQgZGUgZm9ybWUuDQoNClLDqXBvbnNlIDoNCg0KTOKAmWFuYWx5c2UgZGVzIGR1csOpZXMgZGUgc8Opam91ciBkZXMgcGF0aWVudHMgc2Vsb24gdW5lIGxvaSBHYW1tYQ0KY2ktZGVzc291cyBtb250cmUgcXVlLCBiaWVuIHF1ZSBsYSBkdXLDqWUgbW95ZW5uZSBzb2l0IHRyw6hzIHByb2NoZSBlbnRyZQ0KbGVzIHBhdGllbnRzIHByaW9yaXRhaXJlcyAo4omIMSw3NikgZXQgbm9uIHByaW9yaXRhaXJlcyAo4omIMSw3MiksIGxhDQpkaXNwZXJzaW9uIGRpZmbDqHJlIHNlbnNpYmxlbWVudC4gTGVzIHBhdGllbnRzIHByaW9yaXRhaXJlcyBwcsOpc2VudGVudA0KdW5lIHZhcmlhbmNlIHBsdXMgw6lsZXbDqWUgKDEsNjYgY29udHJlIDAsOTEpIGV0IHVuIGNvZWZmaWNpZW50IGRlDQp2YXJpYXRpb24gcGx1cyBpbXBvcnRhbnQgKDAsNzMgY29udHJlIDAsNTUpLCBpbmRpcXVhbnQgdW5lIHBsdXMgZ3JhbmRlDQp2YXJpYWJpbGl0w6kgcmVsYXRpdmUgZGUgbGV1cnMgZHVyw6llcyBkZSBzw6lqb3VyLiBDZXR0ZSBkaWZmw6lyZW5jZSBzZQ0KcmVmbMOodGUgw6lnYWxlbWVudCBkYW5zIGxlcyBwYXJhbcOodHJlcyBkZSBsYSBsb2kgR2FtbWEgOiBsZSBwYXJhbcOodHJlDQpzaGFwZSBkZXMgcGF0aWVudHMgcHJpb3JpdGFpcmVzICjiiYgxLDg2KSBlc3QgcGx1cyBmYWlibGUgcXVlIGNlbHVpIGRlcw0Kbm9uIHByaW9yaXRhaXJlcyAo4omIMywyNiksIGNlIHF1aSB0cmFkdWl0IHVuZSBkaXN0cmlidXRpb24gcGx1cw0KYXN5bcOpdHJpcXVlIGV0IMOpdGFsw6llLCBhdmVjIHVuZSBxdWV1ZSBwbHVzIGxvbmd1ZSDDoCBkcm9pdGUuIMOAIGzigJlpbnZlcnNlLA0KbGEgZGlzdHJpYnV0aW9uIGRlcyBub24gcHJpb3JpdGFpcmVzIGVzdCBwbHVzIGNvbmNlbnRyw6llIGF1dG91ciBkZSBsYQ0KbW95ZW5uZS4gQWluc2ksIG3Dqm1lIHNpIGxlcyBkdXLDqWVzIG1veWVubmVzIHNvbnQgc2ltaWxhaXJlcywgbGVzDQpwYXRpZW50cyBwcmlvcml0YWlyZXMgbW9udHJlbnQgZGVzIHPDqWpvdXJzIHBsdXMgaMOpdMOpcm9nw6huZXMsIGNlIHF1aSBwZXV0DQphdm9pciBkZXMgaW1wbGljYXRpb25zIHBvdXIgbGEgcGxhbmlmaWNhdGlvbiBob3NwaXRhbGnDqHJlIGV0IGxhIGdlc3Rpb24NCmRlcyByZXNzb3VyY2VzLg0KDQpgYGB7cn0NCiNfRW50cmV6IHZvdHJlIGNvZGUgUiBpY2lfDQoNCmRhdGFfcGF0aWVudCA8LSBkYXRhX3JhdyAlPiUNCiAgbXV0YXRlKA0KICAgIGRhdGV0aW1lX2JlZ2luID0geW1kX2htcyhgVGltZXN0YW1wIHN0YXJ0YCksDQogICAgZGF0ZXRpbWVfZW5kICAgPSB5bWRfaG1zKGBUaW1lc3RhbXAgZW5kYCkNCiAgKSAlPiUNCiAgZ3JvdXBfYnkoSUQpICU+JQ0KICBzdW1tYXJpc2UoDQogICAgYXJyaXZhbF90aW1lID0gbWluKGRhdGV0aW1lX2JlZ2luLCBuYS5ybSA9IFRSVUUpLA0KICAgIGRlcGFydHVyZV90aW1lID0gbWF4KGRhdGV0aW1lX2VuZCwgbmEucm0gPSBUUlVFKSwNCiAgICBwcmlvcml0YWlyZSA9IGlmZWxzZShhbnkoZ3JlcGwoIlBSSU8iLCBBY3Rpdml0eV9ERVRBSUxTKSksICJPdWkiLCAiTm9uIiksDQogICAgLmdyb3VwcyA9ICJkcm9wIg0KICApICU+JQ0KICBtdXRhdGUoDQogICAgZHVyZWVfc2Vqb3VyID0gYXMubnVtZXJpYyhkaWZmdGltZShkZXBhcnR1cmVfdGltZSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFycml2YWxfdGltZSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHVuaXRzID0gImhvdXJzIikpDQogICkNCg0KZHVyZWVfcHJpbyA8LSBkYXRhX3BhdGllbnQgJT4lDQogIGZpbHRlcihwcmlvcml0YWlyZSA9PSAiT3VpIikgJT4lDQogIHB1bGwoZHVyZWVfc2Vqb3VyKQ0KDQpkdXJlZV9ub25fcHJpbyA8LSBkYXRhX3BhdGllbnQgJT4lDQogIGZpbHRlcihwcmlvcml0YWlyZSA9PSAiTm9uIikgJT4lDQogIHB1bGwoZHVyZWVfc2Vqb3VyKQ0KDQojIE5ldHRveWFnZQ0KZHVyZWVfcHJpbyA8LSBkdXJlZV9wcmlvW2R1cmVlX3ByaW8gPiAwICYgIWlzLm5hKGR1cmVlX3ByaW8pXQ0KZHVyZWVfbm9uX3ByaW8gPC0gZHVyZWVfbm9uX3ByaW9bZHVyZWVfbm9uX3ByaW8gPiAwICYgIWlzLm5hKGR1cmVlX25vbl9wcmlvKV0NCg0KbGlicmFyeShNQVNTKQ0KDQpmaXRfZ2FtbWFfcHJpbyA8LSBmaXRkaXN0cihkdXJlZV9wcmlvLCAiZ2FtbWEiKQ0KZml0X2dhbW1hX25vbl9wcmlvIDwtIGZpdGRpc3RyKGR1cmVlX25vbl9wcmlvLCAiZ2FtbWEiKQ0KDQpwYXJhbXNfZ2FtbWEgPC0gZGF0YS5mcmFtZSgNCiAgR3JvdXBlID0gYygiUHJpb3JpdGFpcmVzIiwgIk5vbiBwcmlvcml0YWlyZXMiKSwNCiAgU2hhcGUgPSBjKGZpdF9nYW1tYV9wcmlvJGVzdGltYXRlWyJzaGFwZSJdLA0KICAgICAgICAgICAgZml0X2dhbW1hX25vbl9wcmlvJGVzdGltYXRlWyJzaGFwZSJdKSwNCiAgUmF0ZSA9IGMoZml0X2dhbW1hX3ByaW8kZXN0aW1hdGVbInJhdGUiXSwNCiAgICAgICAgICAgZml0X2dhbW1hX25vbl9wcmlvJGVzdGltYXRlWyJyYXRlIl0pDQopDQoNCnBhcmFtc19nYW1tYQ0KDQptb21lbnRzX2dhbW1hIDwtIGRhdGEuZnJhbWUoDQogIEdyb3VwZSA9IGMoIlByaW9yaXRhaXJlcyIsICJOb24gcHJpb3JpdGFpcmVzIiksDQogIEVzcGVyYW5jZSA9IGMoDQogICAgZml0X2dhbW1hX3ByaW8kZXN0aW1hdGVbInNoYXBlIl0gLyBmaXRfZ2FtbWFfcHJpbyRlc3RpbWF0ZVsicmF0ZSJdLA0KICAgIGZpdF9nYW1tYV9ub25fcHJpbyRlc3RpbWF0ZVsic2hhcGUiXSAvIGZpdF9nYW1tYV9ub25fcHJpbyRlc3RpbWF0ZVsicmF0ZSJdDQogICksDQogIFZhcmlhbmNlID0gYygNCiAgICBmaXRfZ2FtbWFfcHJpbyRlc3RpbWF0ZVsic2hhcGUiXSAvIGZpdF9nYW1tYV9wcmlvJGVzdGltYXRlWyJyYXRlIl1eMiwNCiAgICBmaXRfZ2FtbWFfbm9uX3ByaW8kZXN0aW1hdGVbInNoYXBlIl0gLyBmaXRfZ2FtbWFfbm9uX3ByaW8kZXN0aW1hdGVbInJhdGUiXV4yDQogICksDQogIENvZWZmaWNpZW50X3ZhcmlhdGlvbiA9IGMoDQogICAgMSAvIHNxcnQoZml0X2dhbW1hX3ByaW8kZXN0aW1hdGVbInNoYXBlIl0pLA0KICAgIDEgLyBzcXJ0KGZpdF9nYW1tYV9ub25fcHJpbyRlc3RpbWF0ZVsic2hhcGUiXSkNCiAgKQ0KKQ0KDQptb21lbnRzX2dhbW1hDQoNCmhpc3QoZHVyZWVfcHJpbywgcHJvYiA9IFRSVUUsIGJyZWFrcyA9IDMwLA0KICAgICBjb2wgPSByZ2IoMSwwLDAsMC4zNSksDQogICAgIHhsaW0gPSByYW5nZShjKGR1cmVlX3ByaW8sIGR1cmVlX25vbl9wcmlvKSksDQogICAgIG1haW4gPSAiQ29tcGFyYWlzb24gZGVzIGR1csOpZXMgZGUgc8Opam91ciDigJMgTG9pIEdhbW1hIiwNCiAgICAgeGxhYiA9ICJEdXLDqWUgKGhldXJlcykiKQ0KDQpjdXJ2ZShkZ2FtbWEoeCwNCiAgICAgICAgICAgICBzaGFwZSA9IGZpdF9nYW1tYV9wcmlvJGVzdGltYXRlWyJzaGFwZSJdLA0KICAgICAgICAgICAgIHJhdGUgID0gZml0X2dhbW1hX3ByaW8kZXN0aW1hdGVbInJhdGUiXSksDQogICAgICBjb2wgPSAicmVkIiwgbHdkID0gMiwgYWRkID0gVFJVRSkNCg0KaGlzdChkdXJlZV9ub25fcHJpbywgcHJvYiA9IFRSVUUsIGJyZWFrcyA9IDMwLA0KICAgICBjb2wgPSByZ2IoMCwwLDEsMC4zNSksDQogICAgIGFkZCA9IFRSVUUpDQoNCmN1cnZlKGRnYW1tYSh4LA0KICAgICAgICAgICAgIHNoYXBlID0gZml0X2dhbW1hX25vbl9wcmlvJGVzdGltYXRlWyJzaGFwZSJdLA0KICAgICAgICAgICAgIHJhdGUgID0gZml0X2dhbW1hX25vbl9wcmlvJGVzdGltYXRlWyJyYXRlIl0pLA0KICAgICAgY29sID0gImJsdWUiLCBsd2QgPSAyLCBhZGQgPSBUUlVFKQ0KDQpsZWdlbmQoInRvcHJpZ2h0IiwNCiAgICAgICBsZWdlbmQgPSBjKCJQcmlvcml0YWlyZXMiLCAiTm9uIHByaW9yaXRhaXJlcyIpLA0KICAgICAgIGNvbCA9IGMoInJlZCIsICJibHVlIiksDQogICAgICAgbHdkID0gMikNCg0KIyBRLVEgcGxvdCBwb3VyIHByaW9yaXRhaXJlcw0KcGFyKG1mcm93ID0gYygxLCAyKSkNCg0KIyBQcmlvcml0YWlyZXMNCnFxcGxvdChxZ2FtbWEocHBvaW50cyhsZW5ndGgoZHVyZWVfcHJpbykpLA0KICAgICAgICAgICAgICBzaGFwZSA9IGZpdF9nYW1tYV9wcmlvJGVzdGltYXRlWyJzaGFwZSJdLA0KICAgICAgICAgICAgICByYXRlID0gZml0X2dhbW1hX3ByaW8kZXN0aW1hdGVbInJhdGUiXSksDQogICAgICAgZHVyZWVfcHJpbywNCiAgICAgICBtYWluID0gIlEtUSBQbG90IC0gUHJpb3JpdGFpcmVzIiwNCiAgICAgICB4bGFiID0gIlF1YW50aWxlcyB0aMOpb3JpcXVlcyAoR2FtbWEpIiwNCiAgICAgICB5bGFiID0gIlF1YW50aWxlcyBvYnNlcnbDqXMiKQ0KYWJsaW5lKDAsIDEsIGNvbCA9ICJyZWQiKQ0KDQojIE5vbi1wcmlvcml0YWlyZXMNCnFxcGxvdChxZ2FtbWEocHBvaW50cyhsZW5ndGgoZHVyZWVfbm9uX3ByaW8pKSwNCiAgICAgICAgICAgICAgc2hhcGUgPSBmaXRfZ2FtbWFfbm9uX3ByaW8kZXN0aW1hdGVbInNoYXBlIl0sDQogICAgICAgICAgICAgIHJhdGUgPSBmaXRfZ2FtbWFfbm9uX3ByaW8kZXN0aW1hdGVbInJhdGUiXSksDQogICAgICAgZHVyZWVfbm9uX3ByaW8sDQogICAgICAgbWFpbiA9ICJRLVEgUGxvdCAtIE5vbiBwcmlvcml0YWlyZXMiLA0KICAgICAgIHhsYWIgPSAiUXVhbnRpbGVzIHRow6lvcmlxdWVzIChHYW1tYSkiLA0KICAgICAgIHlsYWIgPSAiUXVhbnRpbGVzIG9ic2VydsOpcyIpDQphYmxpbmUoMCwgMSwgY29sID0gImJsdWUiKQ0KDQpwYXIobWZyb3cgPSBjKDEsIDEpKQ0KYGBgDQoNCiMjIyBRdWVzdGlvbiA0KSBNb2TDqWxpc2F0aW9uIGRlIGxhIGR1csOpZSBkZSBzw6lqb3VyIHBhciByw6lncmVzc2lvbiBsaW7DqWFpcmUgKC8zKQ0KDQpBIGwnYWlkZSBkZSBsYSBmb25jdGlvbiAqbG0qLCBjb25zdHJ1aXNleiBldCBhbmFseXNleiB1biBtb2TDqGxlIGRlDQpyw6lncmVzc2lvbiBsaW7DqWFpcmUgZXN0aW1hbnQgbGEgZHVyw6llIGRlIHPDqWpvdXIgcXVpIHByZW5uZW50IGVuIHZhcmlhYmxlDQpkJ2VudHLDqWUgbGUgYmxvYyBkZSAyaCBbOGg7MTBoXSwgLi4uLCBdMTZoOzE4aF0uDQoNClZvaWNpIHVuIHBldGl0IGV4ZW1wbGUgZCd1dGlsaXNhdGlvbiA6DQoNCmBgYHtyfQ0KWSA9IGMoMSwgMS41LCAyLCAzKQ0KWCA9IGMoIkEiLCJBIiwiQiIsIkIiKQ0KDQptb2RlbCA8LSBsbShZIH4gWCkNCnN1bW1hcnkobW9kZWwpIA0KcHJlZGljdChtb2RlbCkgIyBwcmVkaWN0KG1vZGVsLG5ld2RhdGE9W25ld19kYXRhZnJhbWVdKSBpZiBuZXcgZGF0YQ0KYGBgDQoNClLDqXBvbnNlIDoNCg0KKkVudHJleiB2b3RyZSB0ZXh0ZSBpY2kqDQoNCmBgYHtyfQ0KDQpsaWJyYXJ5KGRwbHlyKQ0KbGlicmFyeShsdWJyaWRhdGUpDQpsaWJyYXJ5KGdncGxvdDIpDQpsaWJyYXJ5KHJlYWR4bCkNCmxpYnJhcnkoYnJvb20pICANCg0KDQojIENoYXJnZW1lbnQgZGVzIGRvbm7DqWVzDQpkZiA8LSByZWFkX2V4Y2VsKCJMb2dfUGF0aWVudF9VUk9fMTIxMTIwMTUueGxzeCIpDQoNCiMgUmVub21tZXIgbGVzIGNvbG9ubmVzDQpkZjEgPC0gZGYgJT4lDQogIHJlbmFtZSgNCiAgICBSZXNzX0h1bWFpbmVzID0gYFJlc3MuIEh1bWFpbmVzYCwNCiAgICBUaW1lc3RhbXBfc3RhcnQgPSBgVGltZXN0YW1wIHN0YXJ0YCwNCiAgICBUaW1lc3RhbXBfZW5kICAgPSBgVGltZXN0YW1wIGVuZGAsDQogICAgRElTVEFOQ0VfUEFSQ09VUlVFID0gYGRpc3RhbmNlIHBhcmNvdXJ1ZWANCiAgKSAlPiUNCiAgbXV0YXRlKA0KICAgIFRpbWVzdGFtcF9zdGFydCA9IGFzLlBPU0lYY3QoVGltZXN0YW1wX3N0YXJ0LCBmb3JtYXQgPSAiJWQvJW0vJVkgJUg6JU06JVMiLCB0eiA9ICJFdXJvcGUvUGFyaXMiKSwNCiAgICBUaW1lc3RhbXBfZW5kICAgPSBhcy5QT1NJWGN0KFRpbWVzdGFtcF9lbmQsIGZvcm1hdCA9ICIlZC8lbS8lWSAlSDolTTolUyIsIHR6ID0gIkV1cm9wZS9QYXJpcyIpDQogICkNCg0KIyBDYWxjdWxlciBsYSBkdXLDqWUgZGUgc8Opam91ciBldCBsJ2hldXJlIGQnYXJyaXbDqWUgcG91ciBjaGFxdWUgcGF0aWVudA0KdGVtcHNfc3lzdGVtZSA8LSBkZjEgJT4lDQogIGdyb3VwX2J5KElEKSAlPiUNCiAgc3VtbWFyaXNlKA0KICAgIGVudHJlZSA9IG1pbihUaW1lc3RhbXBfc3RhcnQsIG5hLnJtID0gVFJVRSksDQogICAgc29ydGllID0gbWF4KFRpbWVzdGFtcF9lbmQsIG5hLnJtID0gVFJVRSksDQogICAgLmdyb3VwcyA9ICJkcm9wIg0KICApICU+JQ0KICBtdXRhdGUoDQogICAgVyA9IGFzLm51bWVyaWMoZGlmZnRpbWUoc29ydGllLCBlbnRyZWUsIHVuaXRzID0gImhvdXJzIikpLA0KICAgIGhldXJlX2VudHJlZSA9IGhvdXIoZW50cmVlKSArIG1pbnV0ZShlbnRyZWUpLzYwDQogICkNCg0KIyBDcsOpZXIgbGEgdmFyaWFibGUgYmxvY18yaCBzZWxvbiBsJ2hldXJlIGQnYXJyaXbDqWUNCnRlbXBzX3N5c3RlbWUgPC0gdGVtcHNfc3lzdGVtZSAlPiUNCiAgbXV0YXRlKA0KICAgIGJsb2NfMmggPSBjYXNlX3doZW4oDQogICAgICBoZXVyZV9lbnRyZWUgPj0gOCAmIGhldXJlX2VudHJlZSA8IDEwIH4gIlswOGg7MTBoXSIsDQogICAgICBoZXVyZV9lbnRyZWUgPj0gMTAgJiBoZXVyZV9lbnRyZWUgPCAxMiB+ICJbMTBoOzEyaF0iLA0KICAgICAgaGV1cmVfZW50cmVlID49IDEyICYgaGV1cmVfZW50cmVlIDwgMTQgfiAiWzEyaDsxNGhdIiwNCiAgICAgIGhldXJlX2VudHJlZSA+PSAxNCAmIGhldXJlX2VudHJlZSA8IDE2IH4gIlsxNGg7MTZoXSIsDQogICAgICBoZXVyZV9lbnRyZWUgPj0gMTYgJiBoZXVyZV9lbnRyZWUgPCAxOCB+ICJbMTZoOzE4aF0iLA0KICAgICAgVFJVRSB+IE5BX2NoYXJhY3Rlcl8NCiAgICApDQogICkgJT4lDQogIGZpbHRlcighaXMubmEoYmxvY18yaCksIFcgPiAwLCAhaXMubmEoVykpDQoNCnRlbXBzX3N5c3RlbWUgPC0gdGVtcHNfc3lzdGVtZSAlPiUNCiAgbXV0YXRlKA0KICAgIGJsb2NfMmggPSBmYWN0b3IoYmxvY18yaCwgDQogICAgICAgICAgICAgICAgICAgICBsZXZlbHMgPSBjKCJbMDhoOzEwaF0iLCAiWzEwaDsxMmhdIiwgIlsxMmg7MTRoXSIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiWzE0aDsxNmhdIiwgIlsxNmg7MThoXSIpKQ0KICApDQoNCiMgVsOpcmlmaWNhdGlvbg0KaGVhZCh0ZW1wc19zeXN0ZW1lKQ0Kc3VtbWFyeSh0ZW1wc19zeXN0ZW1lJFcpDQoNCm1vZGVsMSA8LSBsbShXIH4gYmxvY18yaCwgZGF0YSA9IHRlbXBzX3N5c3RlbWUpDQpzdW1tYXJ5KG1vZGVsMSkNCg0KIyBQcsOpZGljdGlvbnMNCnRlbXBzX3N5c3RlbWUkcHJlZF9XMSA8LSBwcmVkaWN0KG1vZGVsMSkNCg0KIyBWaXN1YWxpc2F0aW9uDQpnZ3Bsb3QodGVtcHNfc3lzdGVtZSwgYWVzKHggPSBibG9jXzJoLCB5ID0gVykpICsNCiAgZ2VvbV9qaXR0ZXIod2lkdGggPSAwLjIsIGhlaWdodCA9IDAsIGFscGhhID0gMC41LCBjb2xvciA9ICJsaWdodGJsdWUiLCBzaXplID0gMikgKw0KICBnZW9tX3BvaW50KGFlcyh5ID0gcHJlZF9XMSksIGNvbG9yID0gInJlZCIsIHNpemUgPSA0LCBzaGFwZSA9IDE3KSArDQogIHN0YXRfc3VtbWFyeShmdW4gPSBtZWFuLCBnZW9tID0gInBvaW50IiwgY29sb3IgPSAiZGFya2dyZWVuIiwgc2l6ZSA9IDQsIHNoYXBlID0gMTgpICsNCiAgbGFicygNCiAgICB0aXRsZSA9ICJNb2TDqGxlIDEgOiBEdXLDqWUgZGUgc8Opam91ciBzZWxvbiBsZSBibG9jIGhvcmFpcmUiLA0KICAgIHN1YnRpdGxlID0gIlRyaWFuZ2xlcyByb3VnZXMgPSBwcsOpZGljdGlvbnMgfCBMb3NhbmdlcyB2ZXJ0cyA9IG1veWVubmVzIG9ic2VydsOpZXMiLA0KICAgIHggPSAiQmxvYyBob3JhaXJlIGQnYXJyaXbDqWUiLA0KICAgIHkgPSAiRHVyw6llIGRlIHPDqWpvdXIgVyAoaGV1cmVzKSINCiAgKSArDQogIHRoZW1lX21pbmltYWwoKSArDQogIHRoZW1lKA0KICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoZmFjZSA9ICJib2xkIiwgc2l6ZSA9IDE0KSwNCiAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDApDQogICkNCg0KcGxvdChtb2RlbDEsIHdoaWNoID0gMSwgbWFpbiA9ICJNb2TDqGxlIDEgLSBSw6lzaWR1cyB2cyBWYWxldXJzIGFqdXN0w6llcyIpDQpwbG90KG1vZGVsMSwgd2hpY2ggPSAyLCBtYWluID0gIk1vZMOobGUgMSAtIFEtUSBwbG90IikNCnBsb3QobW9kZWwxLCB3aGljaCA9IDMsIG1haW4gPSAiTW9kw6hsZSAxIC0gU2NhbGUtTG9jYXRpb24iKQ0KcGxvdChtb2RlbDEsIHdoaWNoID0gNSwgbWFpbiA9ICJNb2TDqGxlIDEgLSBSw6lzaWR1cyB2cyBMZXZpZXIiKQ0KDQojIElkZW50aWZpZXIgbGVzIHBhdGllbnRzIHByaW9yaXRhaXJlcw0KdGVtcHNfc3lzdGVtZTIgPC0gdGVtcHNfc3lzdGVtZSAlPiUNCiAgbGVmdF9qb2luKA0KICAgIGRmMSAlPiUNCiAgICAgIGdyb3VwX2J5KElEKSAlPiUNCiAgICAgIHN1bW1hcmlzZShwcmlvcml0YWlyZSA9IGlmZWxzZShhbnkoZ3JlcGwoIlBSSU8iLCBBY3Rpdml0eV9ERVRBSUxTKSksICJPdWkiLCAiTm9uIiksDQogICAgICAgICAgICAgICAgLmdyb3VwcyA9ICJkcm9wIiksDQogICAgYnkgPSAiSUQiDQogICkNCg0KdGVtcHNfc3lzdGVtZTIgPC0gdGVtcHNfc3lzdGVtZTIgJT4lDQogIG11dGF0ZShwcmlvcml0YWlyZSA9IGZhY3Rvcihwcmlvcml0YWlyZSwgbGV2ZWxzID0gYygiTm9uIiwgIk91aSIpKSkNCg0KIyBWw6lyaWZpY2F0aW9uDQp0YWJsZSh0ZW1wc19zeXN0ZW1lMiRwcmlvcml0YWlyZSkNCg0KIyBNb2TDqGxlIDINCm1vZGVsMiA8LSBsbShXIH4gYmxvY18yaCArIHByaW9yaXRhaXJlLCBkYXRhID0gdGVtcHNfc3lzdGVtZTIpDQpzdW1tYXJ5KG1vZGVsMikNCg0KIyBQcsOpZGljdGlvbnMNCnRlbXBzX3N5c3RlbWUyJHByZWRfVzIgPC0gcHJlZGljdChtb2RlbDIpDQoNCiMgVmlzdWFsaXNhdGlvbg0KZ2dwbG90KHRlbXBzX3N5c3RlbWUyLCBhZXMoeCA9IGJsb2NfMmgsIHkgPSBXLCBjb2xvciA9IHByaW9yaXRhaXJlKSkgKw0KICBnZW9tX2ppdHRlcih3aWR0aCA9IDAuMiwgaGVpZ2h0ID0gMCwgYWxwaGEgPSAwLjQsIHNpemUgPSAyKSArDQogIGdlb21fcG9pbnQoYWVzKHkgPSBwcmVkX1cyKSwgc2hhcGUgPSAxNywgc2l6ZSA9IDQpICsNCiAgZ2VvbV9saW5lKGFlcyh5ID0gcHJlZF9XMiwgZ3JvdXAgPSBwcmlvcml0YWlyZSksIGxpbmV3aWR0aCA9IDEuMikgKw0KICBsYWJzKA0KICAgIHRpdGxlID0gIk1vZMOobGUgMiA6IFcgfiBibG9jXzJoICsgcHJpb3JpdGFpcmUiLA0KICAgIHN1YnRpdGxlID0gIlRyaWFuZ2xlcyA9IHByw6lkaWN0aW9ucyB8IExpZ25lcyBwYXJhbGzDqGxlcyBtb250cmVudCBsJ2VmZmV0IGFkZGl0aWYiLA0KICAgIHggPSAiQmxvYyBob3JhaXJlIGQnYXJyaXbDqWUiLA0KICAgIHkgPSAiRHVyw6llIGRlIHPDqWpvdXIgVyAoaGV1cmVzKSIsDQogICAgY29sb3IgPSAiUHJpb3JpdGFpcmUiDQogICkgKw0KICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYygiTm9uIiA9ICJzdGVlbGJsdWUiLCAiT3VpIiA9ICJ0b21hdG8iKSkgKw0KICB0aGVtZV9taW5pbWFsKCkgKw0KICB0aGVtZSgNCiAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGZhY2UgPSAiYm9sZCIsIHNpemUgPSAxNCksDQogICAgbGVnZW5kLnBvc2l0aW9uID0gInRvcCINCiAgKQ0KDQojIERpYWdub3N0aWNzIFVOIFBBUiBVTg0KcGxvdChtb2RlbDIsIHdoaWNoID0gMSwgbWFpbiA9ICJNb2TDqGxlIDIgLSBSw6lzaWR1cyB2cyBWYWxldXJzIGFqdXN0w6llcyIpDQpwbG90KG1vZGVsMiwgd2hpY2ggPSAyLCBtYWluID0gIk1vZMOobGUgMiAtIFEtUSBwbG90IikNCnBsb3QobW9kZWwyLCB3aGljaCA9IDMsIG1haW4gPSAiTW9kw6hsZSAyIC0gU2NhbGUtTG9jYXRpb24iKQ0KcGxvdChtb2RlbDIsIHdoaWNoID0gNSwgbWFpbiA9ICJNb2TDqGxlIDIgLSBSw6lzaWR1cyB2cyBMZXZpZXIiKQ0KDQptb2RlbHNfY29tcGFyaXNvbiA8LSB0aWJibGUoDQogIE1vZMOobGUgPSBjKCJNb2TDqGxlIDE6IFcgfiBibG9jXzJoIiwgDQogICAgICAgICAgICAgIk1vZMOobGUgMjogVyB+IGJsb2NfMmggKyBwcmlvcml0YWlyZSIpLA0KICBSX3NxdWFyZWQgPSBjKHN1bW1hcnkobW9kZWwxKSRyLnNxdWFyZWQsIA0KICAgICAgICAgICAgICAgIHN1bW1hcnkobW9kZWwyKSRyLnNxdWFyZWQpLA0KICBSX3NxdWFyZWRfYWRqID0gYyhzdW1tYXJ5KG1vZGVsMSkkYWRqLnIuc3F1YXJlZCwgDQogICAgICAgICAgICAgICAgICAgIHN1bW1hcnkobW9kZWwyKSRhZGouci5zcXVhcmVkKSwNCiAgQUlDID0gYyhBSUMobW9kZWwxKSwgQUlDKG1vZGVsMikpLA0KICBCSUMgPSBjKEJJQyhtb2RlbDEpLCBCSUMobW9kZWwyKSkNCikNCg0KcHJpbnQobW9kZWxzX2NvbXBhcmlzb24pDQoNCiMgVGVzdCBkZSBjb21wYXJhaXNvbiAoQU5PVkEpDQphbm92YShtb2RlbDEsIG1vZGVsMikNCg0KbW9kZWwzIDwtIGxtKFcgfiBibG9jXzJoICogcHJpb3JpdGFpcmUsIGRhdGEgPSB0ZW1wc19zeXN0ZW1lMikNCnN1bW1hcnkobW9kZWwzKQ0KDQojIENvbXBhcmFpc29uIGRlcyAzIG1vZMOobGVzDQphbm92YShtb2RlbDEsIG1vZGVsMiwgbW9kZWwzKQ0KDQojIFZpc3VhbGlzYXRpb24gZHUgbW9kw6hsZSAzDQp0ZW1wc19zeXN0ZW1lMiRwcmVkX1czIDwtIHByZWRpY3QobW9kZWwzKQ0KDQpnZ3Bsb3QodGVtcHNfc3lzdGVtZTIsIGFlcyh4ID0gYmxvY18yaCwgeSA9IFcsIGNvbG9yID0gcHJpb3JpdGFpcmUpKSArDQogIGdlb21faml0dGVyKHdpZHRoID0gMC4yLCBoZWlnaHQgPSAwLCBhbHBoYSA9IDAuNCwgc2l6ZSA9IDIpICsNCiAgZ2VvbV9wb2ludChhZXMoeSA9IHByZWRfVzMpLCBzaGFwZSA9IDE3LCBzaXplID0gNCkgKw0KICBnZW9tX2xpbmUoYWVzKHkgPSBwcmVkX1czLCBncm91cCA9IHByaW9yaXRhaXJlKSwgbGluZXdpZHRoID0gMS4yKSArICANCiAgbGFicygNCiAgICB0aXRsZSA9ICJNb2TDqGxlIDMgOiBXIH4gYmxvY18yaCAqIHByaW9yaXRhaXJlIChhdmVjIGludGVyYWN0aW9uKSIsDQogICAgc3VidGl0bGUgPSAiTGVzIGxpZ25lcyBOT04gcGFyYWxsw6hsZXMgaW5kaXF1ZXJhaWVudCB1bmUgaW50ZXJhY3Rpb24iLA0KICAgIHggPSAiQmxvYyBob3JhaXJlIGQnYXJyaXbDqWUiLA0KICAgIHkgPSAiRHVyw6llIGRlIHPDqWpvdXIgVyAoaGV1cmVzKSIsDQogICAgY29sb3IgPSAiUHJpb3JpdGFpcmUiDQogICkgKw0KICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYygiTm9uIiA9ICJzdGVlbGJsdWUiLCAiT3VpIiA9ICJ0b21hdG8iKSkgKw0KICB0aGVtZV9taW5pbWFsKCkgKw0KICB0aGVtZSgNCiAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGZhY2UgPSAiYm9sZCIsIHNpemUgPSAxNCksDQogICAgbGVnZW5kLnBvc2l0aW9uID0gInRvcCINCiAgKQ0KDQpjb2VmX3RhYmxlIDwtIGRhdGEuZnJhbWUoDQogIE1vZMOobGUgPSBjKHJlcCgiTW9kw6hsZSAxIiwgbGVuZ3RoKGNvZWYobW9kZWwxKSkpLA0KICAgICAgICAgICAgIHJlcCgiTW9kw6hsZSAyIiwgbGVuZ3RoKGNvZWYobW9kZWwyKSkpKSwNCiAgVGVybWUgPSBjKG5hbWVzKGNvZWYobW9kZWwxKSksIG5hbWVzKGNvZWYobW9kZWwyKSkpLA0KICBFc3RpbWF0ZSA9IHJvdW5kKGMoY29lZihtb2RlbDEpLCBjb2VmKG1vZGVsMikpLCAzKSwNCiAgU3RkX0Vycm9yID0gcm91bmQoYyhzdW1tYXJ5KG1vZGVsMSkkY29lZmZpY2llbnRzWywgIlN0ZC4gRXJyb3IiXSwNCiAgICAgICAgICAgICAgICAgICAgICBzdW1tYXJ5KG1vZGVsMikkY29lZmZpY2llbnRzWywgIlN0ZC4gRXJyb3IiXSksIDMpLA0KICBQX3ZhbHVlID0gcm91bmQoYyhzdW1tYXJ5KG1vZGVsMSkkY29lZmZpY2llbnRzWywgIlByKD58dHwpIl0sDQogICAgICAgICAgICAgICAgICAgIHN1bW1hcnkobW9kZWwyKSRjb2VmZmljaWVudHNbLCAiUHIoPnx0fCkiXSksIDQpDQopDQoNCmNvZWZfdGFibGUkU2lnbmlmaWNhdGlmIDwtIGlmZWxzZShjb2VmX3RhYmxlJFBfdmFsdWUgPCAwLjA1LCAiKioqIiwgIiIpDQoNCnByaW50KGNvZWZfdGFibGUpDQoNCg0KY2F0KCJcbj09PSBJTlRFUlBSw4lUQVRJT04gREVTIFLDiVNVTFRBVFMgPT09XG5cbiIpDQoNCiMgTW9kw6hsZSAxDQpjYXQoIk1PRMOITEUgMSAoVyB+IGJsb2NfMmgpOlxuIikNCmNhdChzcHJpbnRmKCItIFLCsiA9ICUuM2YgKHNldWxlbWVudCAlLjFmJSUgZGUgbGEgdmFyaWFuY2UgZXhwbGlxdcOpZSlcbiIsIA0KICAgICAgICAgICAgc3VtbWFyeShtb2RlbDEpJHIuc3F1YXJlZCwgc3VtbWFyeShtb2RlbDEpJHIuc3F1YXJlZCAqIDEwMCkpDQpjYXQoIi0gQXVjdW4gYmxvYyBob3JhaXJlIG4nZXN0IHNpZ25pZmljYXRpZiAocCA+IDAuMDUpXG4iKQ0KY2F0KCItIENvbmNsdXNpb246IEwnaGV1cmUgZCdhcnJpdsOpZSBuJ2luZmx1ZW5jZSBQQVMgc2lnbmlmaWNhdGl2ZW1lbnQgbGEgZHVyw6llIGRlIHPDqWpvdXJcblxuIikNCg0KIyBNb2TDqGxlIDINCmNhdCgiTU9Ew4hMRSAyIChXIH4gYmxvY18yaCArIHByaW9yaXRhaXJlKTpcbiIpDQpjYXQoc3ByaW50ZigiLSBSwrIgPSAlLjNmICglLjFmJSUgZGUgbGEgdmFyaWFuY2UgZXhwbGlxdcOpZSlcbiIsIA0KICAgICAgICAgICAgc3VtbWFyeShtb2RlbDIpJHIuc3F1YXJlZCwgc3VtbWFyeShtb2RlbDIpJHIuc3F1YXJlZCAqIDEwMCkpDQpjb2VmX3ByaW8gPC0gY29lZihtb2RlbDIpWyJwcmlvcml0YWlyZU91aSJdDQpjYXQoc3ByaW50ZigiLSBFZmZldCBwcmlvcml0YWlyZTogKyUuMmYgaGV1cmVzIChzb2l0IH4lLjBmIG1pbnV0ZXMpXG4iLCANCiAgICAgICAgICAgIGNvZWZfcHJpbywgY29lZl9wcmlvICogNjApKQ0KDQojIFRlc3QgZGUgc2lnbmlmaWNhdGl2aXTDqQ0KcF9wcmlvIDwtIHN1bW1hcnkobW9kZWwyKSRjb2VmZmljaWVudHNbInByaW9yaXRhaXJlT3VpIiwgIlByKD58dHwpIl0NCmlmIChwX3ByaW8gPCAwLjA1KSB7DQogIGNhdChzcHJpbnRmKCItIENldCBlZmZldCBlc3QgU0lHTklGSUNBVElGIChwID0gJS40ZilcbiIsIHBfcHJpbykpDQp9IGVsc2Ugew0KICBjYXQoc3ByaW50ZigiLSBDZXQgZWZmZXQgbidlc3QgcGFzIHNpZ25pZmljYXRpZiAocCA9ICUuNGYpXG4iLCBwX3ByaW8pKQ0KfQ0KDQpgYGANCg0KTm91cyBhdm9ucyDDqXR1ZGnDqSBs4oCZZWZmZXQgZGUgbOKAmWhldXJlIGTigJlhcnJpdsOpZSBldCBkdSBzdGF0dXQgcHJpb3JpdGFpcmUNCnN1ciBsYSBkdXLDqWUgZGUgc8Opam91ciBlbiBhbmFseXNhbnQgdHJvaXMgbW9kw6hsZXMgc3VjY2Vzc2lmcy4NCg0KTGUgbW9kw6hsZSAxIGNvbnNpZMOocmUgdW5pcXVlbWVudCBsZSBibG9jIGhvcmFpcmUgZOKAmWFycml2w6llLiBMZSBncmFwaGlxdWUNCmFzc29jacOpIG1vbnRyZSB1bmUgbMOpZ8OocmUgYXVnbWVudGF0aW9uIGRlIGxhIGR1csOpZSBtb3llbm5lIGVudHJlIGxlDQptYXRpbiBldCBsZSBtaWxpZXUgZOKAmWFwcsOocy1taWRpLCBzdWl2aWUgZOKAmXVuZSBzdGFiaWxpc2F0aW9uIGVuIGZpbiBkZQ0Kam91cm7DqWUuIENlcGVuZGFudCwgY2V0dGUgdGVuZGFuY2UgcmVzdGUgZmFpYmxlIHBhciByYXBwb3J0IMOgIGxhDQpkaXNwZXJzaW9uIHRyw6hzIGltcG9ydGFudGUgZGVzIGR1csOpZXMgaW5kaXZpZHVlbGxlcyDDoCBs4oCZaW50w6lyaWV1ciBkZQ0KY2hhcXVlIGJsb2MuIExlcyB2YWxldXJzIHByw6lkaXRlcyBwYXIgbGUgbW9kw6hsZSBjb8OvbmNpZGVudCBxdWFzaW1lbnQNCmF2ZWMgbGVzIG1veWVubmVzIG9ic2VydsOpZXMsIGluZGlxdWFudCBxdWUgbGUgbW9kw6hsZSByZXByb2R1aXQgc3VydG91dA0KbGEgdGVuZGFuY2UgY2VudHJhbGUgc2FucyBjYXB0ZXIgbGEgdmFyaWFiaWxpdMOpIGluZGl2aWR1ZWxsZS4gTOKAmWhldXJlDQpk4oCZYXJyaXbDqWUgc2V1bGUgZXhwbGlxdWUgZG9uYyB0csOocyBwZXUgbGEgZHVyw6llIGRlIHPDqWpvdXIuDQoNCkzigJlham91dCBkdSBzdGF0dXQgcHJpb3JpdGFpcmUgc3VyIGxlIG1vZMOobGUgMiBtb250cmUgcXVlIGxlcyBjb3VyYmVzIGRlcw0KcGF0aWVudHMgcHJpb3JpdGFpcmVzIGV0IG5vbiBwcmlvcml0YWlyZXMgc29udCBwcmVzcXVlIHBhcmFsbMOobGVzLCBhdmVjDQp1biBsw6lnZXIgZMOpY2FsYWdlIHZlcnRpY2FsLiBMZXMgcGF0aWVudHMgcHJpb3JpdGFpcmVzIHByw6lzZW50ZW50IHVuZQ0KZHVyw6llIG1veWVubmUgbMOpZ8OocmVtZW50IHN1cMOpcmlldXJlLCBtYWlzIGNldCDDqWNhcnQgcmVzdGUgdHLDqHMgZmFpYmxlIGV0DQpsYXJnZW1lbnQgbWFzcXXDqSBwYXIgbGEgZGlzcGVyc2lvbiBkZXMgZG9ubsOpZXMuIFZpc3VlbGxlbWVudCBldA0Kc3RhdGlzdGlxdWVtZW50LCBsZSBzdGF0dXQgcHJpb3JpdGFpcmUgbuKAmWFtw6lsaW9yZSBwYXMgc2lnbmlmaWNhdGl2ZW1lbnQNCmxlIHBvdXZvaXIgZXhwbGljYXRpZiBkdSBtb2TDqGxlLg0KDQpFbiBpbmNsdWFudCBs4oCZaW50ZXJhY3Rpb24gZW50cmUgYmxvYyBob3JhaXJlIGV0IHN0YXR1dCBwcmlvcml0YWlyZSBzdXINCmxlIG1vZMOobGUgMywgb24gb2JzZXJ2ZSBkZXMgZGlmZsOpcmVuY2VzIHRlbXBvcmFpcmVzIGVudHJlIGxlcyBkZXV4DQpncm91cGVzIDogbGEgY291cmJlIGRlcyBwYXRpZW50cyBwcmlvcml0YWlyZXMgc2UgcmFwcHJvY2hlIHB1aXMgZMOpcGFzc2UNCmNlbGxlIGRlcyBub24tcHJpb3JpdGFpcmVzIHN1ciBjZXJ0YWlucyBjcsOpbmVhdXggaG9yYWlyZXMsIGF2YW50IGRlIHNlDQpyZWpvaW5kcmUgw6Agbm91dmVhdS4gTsOpYW5tb2lucywgY2VzIHZhcmlhdGlvbnMgcmVzdGVudCBmYWlibGVzIHBhcg0KcmFwcG9ydCDDoCBsYSB2YXJpYWJpbGl0w6kgaW5kaXZpZHVlbGxlLiBM4oCZaW50ZXJhY3Rpb24gbuKAmWFwcG9ydGUgZG9uYw0KcXXigJl1bmUgZXhwbGljYXRpb24gbWFyZ2luYWxlIHN1cHBsw6ltZW50YWlyZSBldCBuZSBtb2RpZmllIHBhcyBsYQ0KY29uY2x1c2lvbiBnw6luw6lyYWxlIDogbOKAmWhldXJlIGTigJlhcnJpdsOpZSBldCBsZSBzdGF0dXQgcHJpb3JpdGFpcmUNCm7igJlleHBsaXF1ZW50IHF14oCZdW5lIHBhcnQgbWlub3JpdGFpcmUgZGUgbGEgZHVyw6llIGRlIHPDqWpvdXIuDQoNCkxlcyB0cm9pcyBtb2TDqGxlcyBtb250cmVudCBkZSBtYW5pw6hyZSBjb2jDqXJlbnRlIHF1ZSA6DQoNCkxhIHZhcmlhYmlsaXTDqSBpbnRyYS1ibG9jIGRvbWluZSBsYXJnZW1lbnQgbGVzIGRpZmbDqXJlbmNlcyBtb3llbm5lcw0KZW50cmUgZ3JvdXBlcy4gTmkgbOKAmWhldXJlIGTigJlhcnJpdsOpZSBuaSBsZSBzdGF0dXQgcHJpb3JpdGFpcmUsIG5pIGxldXINCmludGVyYWN0aW9uLCBuZSBzdHJ1Y3R1cmVudCByw6llbGxlbWVudCBsYSBkdXLDqWUgZGUgc8Opam91ci4NCg0KROKAmWF1dHJlcyBmYWN0ZXVycyBub24gaW5jbHVzIGRhbnMgbGVzIG1vZMOobGVzIChjb21wbGV4aXTDqSBkZXMgY2FzLCBhY3Rlcw0KcsOpYWxpc8Opcywgb3JnYW5pc2F0aW9uIGludGVybmUpIHNvbnQgcHJvYmFibGVtZW50IGRlcyBwcmluY2lwYXV4DQpkw6l0ZXJtaW5hbnRzIGR1IHRlbXBzIHBhc3PDqSBkYW5zIGxlIHN5c3TDqG1lLg0KDQpDZXMgb2JzZXJ2YXRpb25zIHNvbnQgY29ow6lyZW50ZXMgYXZlYyBsZSBmYWlibGUgUsKyIGRlIGNoYXF1ZSBtb2TDqGxlIGV0DQpzb3VsaWduZW50IGxlIHLDtGxlIHNlY29uZGFpcmUgZGUgbOKAmWhldXJlIGTigJlhcnJpdsOpZSBldCBkdSBzdGF0dXQNCnByaW9yaXRhaXJlIGRhbnMgbOKAmWV4cGxpY2F0aW9uIGRlcyBkdXLDqWVzIGRlIHPDqWpvdXIuDQoNCmBgYHtyfQ0KbGlicmFyeShkcGx5cikNCmxpYnJhcnkobHVicmlkYXRlKQ0KDQojIENyw6llIGxhIHZhcmlhYmxlIGJsb2MgMmggc2Vsb24gbCdoZXVyZSBkJ2Fycml2w6llDQp0ZW1wc19zeXN0ZW1lIDwtIHRlbXBzX3N5c3RlbWUgJT4lDQogIG11dGF0ZSgNCiAgICBoZXVyZV9lbnRyZWUgPSBob3VyKGVudHJlZSksDQogICAgYmxvY18yaCA9IGNhc2Vfd2hlbigNCiAgICAgIGhldXJlX2VudHJlZSA+PSA4ICYgaGV1cmVfZW50cmVlIDwgMTAgfiAiWzA4aDsxMGhdIiwNCiAgICAgIGhldXJlX2VudHJlZSA+PSAxMCAmIGhldXJlX2VudHJlZSA8IDEyIH4gIlsxMGg7MTJoXSIsDQogICAgICBoZXVyZV9lbnRyZWUgPj0gMTIgJiBoZXVyZV9lbnRyZWUgPCAxNCB+ICJbMTJoOzE0aF0iLA0KICAgICAgaGV1cmVfZW50cmVlID49IDE0ICYgaGV1cmVfZW50cmVlIDwgMTYgfiAiWzE0aDsxNmhdIiwNCiAgICAgIGhldXJlX2VudHJlZSA+PSAxNiAmIGhldXJlX2VudHJlZSA8IDE4IH4gIlsxNmg7MThoXSIsDQogICAgICBUUlVFIH4gTkFfY2hhcmFjdGVyXw0KICAgICkNCiAgKSAlPiUNCiAgZmlsdGVyKCFpcy5uYShibG9jXzJoKSkgIA0KDQp0ZW1wc19zeXN0ZW1lDQoNCm1vZGVsMSA8LSBsbShXIH4gYmxvY18yaCwgZGF0YSA9IHRlbXBzX3N5c3RlbWUpDQpzdW1tYXJ5KG1vZGVsMSkNCg0KIyBwcsOpZGljdGlvbnMgc3VyIGxlIG3Dqm1lIGRhdGFzZXQNCnRlbXBzX3N5c3RlbWUkcHJlZF9XMSA8LSBwcmVkaWN0KG1vZGVsMSkNCmBgYA0KDQpM4oCZYW5hbHlzZSBkdSBtb2TDqGxlIGxpbsOpYWlyZSBzaW1wbGUgZGUgbGEgZHVyw6llIGRlIHPDqWpvdXIgZW4gZm9uY3Rpb24gZHUNCmJsb2MgaG9yYWlyZSBk4oCZYXJyaXbDqWUgbW9udHJlIHF1ZSBsYSBkdXLDqWUgbW95ZW5uZSBwb3VyIGxlIGJsb2MgOGjigJMxMGgNCmVzdCBkZSAwLDk0IGggKEludGVyY2VwdCwgcCA9IDAsMDQ0KS4gUGFyIHJhcHBvcnQgw6AgY2UgYmxvYyBkZQ0KcsOpZsOpcmVuY2UsIGxlcyBkdXLDqWVzIG1veWVubmVzIGF1Z21lbnRlbnQgZGUgMCw2MyBoIHBvdXIgMTBo4oCTMTJoIChwID0NCjAsMjMpLCAwLDc2IGggcG91ciAxMmjigJMxNGggKHAgPSAwLDE5KSwgMSwxNCBoIHBvdXIgMTRo4oCTMTZoIChwID0gMCwwNDYpDQpldCAxLDAxIGggcG91ciAxNmjigJMxOGggKHAgPSAwLDA2MykuIFNldWwgbGUgYmxvYyAxNGjigJMxNmggZXN0DQpzaWduaWZpY2F0aXZlbWVudCBkaWZmw6lyZW50IGR1IGJsb2MgZGUgcsOpZsOpcmVuY2UsIHRhbmRpcyBxdWUgMTZo4oCTMThoDQptb250cmUgdW5lIHRlbmRhbmNlLiBMZSBtb2TDqGxlIGV4cGxpcXVlIHBldSBsYSB2YXJpYWJpbGl0w6kgZ2xvYmFsZSAoUsKyID0NCjAsMDgxKSwgY2UgcXVpLCBjb21iaW7DqSDDoCBkZXMgcsOpc2lkdXMgdHLDqHMgZGlzcGVyc8OpcyAoZGUgLTEsNTcgw6AgMyw4MA0KaCksIGluZGlxdWUgcXVlIGzigJloZXVyZSBk4oCZYXJyaXbDqWUgYSB1biBlZmZldCBmYWlibGUgc3VyIGxhIGR1csOpZSBkZQ0Kc8Opam91ciBldCBxdWUgbGEgdmFyaWFiaWxpdMOpIGluZGl2aWR1ZWxsZSBkb21pbmUgbGFyZ2VtZW50Lg0KDQpgYGB7cn0NCmxpYnJhcnkoZ2dwbG90MikNCg0KZ2dwbG90KHRlbXBzX3N5c3RlbWUsIGFlcyh4ID0gYmxvY18yaCwgeSA9IFcpKSArDQogIGdlb21faml0dGVyKHdpZHRoID0gMC4yLCBoZWlnaHQgPSAwLCBhbHBoYSA9IDAuNSwgY29sb3IgPSAiYmx1ZSIpICsNCiAgZ2VvbV9wb2ludChhZXMoeSA9IHByZWRfVzEpLCBjb2xvciA9ICJyZWQiLCBzaXplID0gMykgKw0KICBzdGF0X3N1bW1hcnkoZnVuID0gbWVhbiwgZ2VvbSA9ICJwb2ludCIsIGNvbG9yID0gImRhcmtncmVlbiIsIHNpemUgPSAzKSArDQogIGxhYnMoDQogICAgdGl0bGUgPSAiRHVyw6llIGRlIHPDqWpvdXIgc2Vsb24gbGUgYmxvYyAyaCIsDQogICAgeCA9ICJCbG9jIGhvcmFpcmUgZCdhcnJpdsOpZSIsDQogICAgeSA9ICJEdXLDqWUgZGUgc8Opam91ciBXIChoZXVyZXMpIg0KICApICsNCiAgdGhlbWVfbWluaW1hbCgpDQoNCmBgYA0KDQpMZXMgcG9pbnRzIHZlcnRzIGR1IGdyYXBoaXF1ZSByZXByw6lzZW50ZW50IGxhIGR1csOpZSBtb3llbm5lIGRlIHPDqWpvdXINCnBhciBibG9jIGhvcmFpcmUuIE9uIG9ic2VydmUgZW52aXJvbiAxIGggcG91ciBsZSBibG9jIDho4oCTMTBoLCAxLDUgaCBwb3VyDQoxMGjigJMxMmggZXQgMTJo4oCTMTRoLCBldCB1biBwZXUgcGx1cyBkZSAyIGggcG91ciAxNGjigJMxNmgsIGF2ZWMgZW52aXJvbiAyIGgNCsOpZ2FsZW1lbnQgcG91ciAxNmjigJMxOGguIFF1ZWxxdWVzIHZhbGV1cnMgYXR5cGlxdWVzIHJlc3NvcnRlbnQgOiB1bg0KcGF0aWVudCBwcm9jaGUgZGUgNiBoIHBvdXIgbGUgYmxvYyAxNGjigJMxNmgsIGV0IGRldXggcGF0aWVudHMgYXV0b3VyIGRlIDUNCmggcG91ciAxNmjigJMxOGguIEVuIGRlaG9ycyBkZSBjZXMgY2FzIGlzb2zDqXMsIGxlcyBkdXLDqWVzIHNlIHJlZ3JvdXBlbnQNCmF1dG91ciBkZSBsYSBtb3llbm5lLCBtb250cmFudCBxdWUgbWFsZ3LDqSBsYSBwcsOpc2VuY2UgZGUgcXVlbHF1ZXMNCmV4dHLDqm1lcywgbGEgbWFqb3JpdMOpIGRlcyBwYXRpZW50cyByZXN0ZSBjb25jZW50csOpZSBhdXRvdXIgZGUgbGENCnRlbmRhbmNlIGNlbnRyYWxlLg0KDQpSZWZhaXRlcyB1biBkZXV4acOobWUgbW9kw6hsZSBsaW7DqWFpcmUgaW50w6lncmFudCB1bmUgdmFyaWFibGUgY2F0w6lnb3JpZWxsZQ0KcXVpIGluZGlxdWUgc2kgbGUgcGF0aWVudCBlc3QgcHJpb3JpdGFpcmUuDQoNClLDqXBvbnNlIDoNCg0KKkVudHJleiB2b3RyZSB0ZXh0ZSBpY2kqDQoNCmBgYHtyfQ0KZGYxIDwtIGRmMSAlPiUNCiAgbXV0YXRlKHByaW9yaXRhaXJlID0gaWZlbHNlKGdyZXBsKCJQUklPIiwgQWN0aXZpdHlfREVUQUlMUyksICJPdWkiLCAiTm9uIikpDQoNCnRlbXBzX3N5c3RlbWUyIDwtIHRlbXBzX3N5c3RlbWUgJT4lDQogIGRwbHlyOjpsZWZ0X2pvaW4oDQogICAgZGYxICU+JQ0KICAgICAgZ3JvdXBfYnkoSUQpICU+JQ0KICAgICAgc3VtbWFyaXNlKHByaW9yaXRhaXJlID0gaWZlbHNlKGFueShncmVwbCgiUFJJTyIsIEFjdGl2aXR5X0RFVEFJTFMpKSwgIk91aSIsICJOb24iKSksDQogICAgYnkgPSAiSUQiDQogICkNCg0KZGYxICU+JSBmaWx0ZXIoZ3JlcGwoIlBSSU8iLCBBY3Rpdml0eV9ERVRBSUxTKSkNCnRhYmxlKHRlbXBzX3N5c3RlbWUyJHByaW9yaXRhaXJlKQ0KbW9kZWwyIDwtIGxtKFcgfiBibG9jXzJoICsgcHJpb3JpdGFpcmUsIGRhdGEgPSB0ZW1wc19zeXN0ZW1lMikNCnN1bW1hcnkobW9kZWwyKQ0KDQojIHByw6lkaWN0aW9ucw0KdGVtcHNfc3lzdGVtZTIkcHJlZF9XMiA8LSBwcmVkaWN0KG1vZGVsMikNCmBgYA0KDQpM4oCZYW5hbHlzZSBkdSBtb2TDqGxlIGluY2x1YW50IGxlIGJsb2MgaG9yYWlyZSBldCBsZSBzdGF0dXQgcHJpb3JpdGFpcmUNCm1vbnRyZSBxdWUgbGEgZHVyw6llIG1veWVubmUgZGUgc8Opam91ciBwb3VyIHVuIHBhdGllbnQgbm9uIHByaW9yaXRhaXJlDQphcnJpdsOpIGVudHJlIDhoIGV0IDEwaCBlc3QgZOKAmWVudmlyb24gMCw5MyBoIChwID0gMCwwNDgpLiBMZXMgZHVyw6llcw0KYXVnbWVudGVudCBsw6lnw6hyZW1lbnQgcG91ciBsZXMgYmxvY3Mgc3VpdmFudHMgOiArMCw2MyBoIHBvdXIgMTBo4oCTMTJoIChwDQo9IDAsMjQpLCArMCw3NiBoIHBvdXIgMTJo4oCTMTRoIChwID0gMCwyMCksICsxLDEzIGggcG91ciAxNGjigJMxNmggKHAgPQ0KMCwwNTApIGV0ICsxLDAxIGggcG91ciAxNmjigJMxOGggKHAgPSAwLDA2NikuIEVuIHJldmFuY2hlLCBsZSBzdGF0dXQNCnByaW9yaXRhaXJlIG7igJlhIHByYXRpcXVlbWVudCBhdWN1biBlZmZldCBzdXIgbGEgZHVyw6llIGRlIHPDqWpvdXIgKCswLDAyNQ0KaCwgcCA9IDAsOTM2KS4gTGUgbW9kw6hsZSBleHBsaXF1ZSB0csOocyBwZXUgbGEgdmFyaWFiaWxpdMOpIG9ic2VydsOpZSAoUsKyID0NCjAsMDgxLCBhanVzdMOpID0gMCwwMDIpLCBldCBsZXMgcsOpc2lkdXMgbW9udHJlbnQgdW5lIGZvcnRlIGRpc3BlcnNpb24NCmluZGl2aWR1ZWxsZSAoLTEsNTYgw6AgMyw3OCBoKS4gR2xvYmFsZW1lbnQsIGNlcyByw6lzdWx0YXRzIGNvbmZpcm1lbnQgcXVlDQpuaSBs4oCZaGV1cmUgZOKAmWFycml2w6llIG5pIGxlIHN0YXR1dCBwcmlvcml0YWlyZSBuZSBwZXJtZXR0ZW50IGRlIHByw6lkaXJlDQpkZSBtYW5pw6hyZSBzaWduaWZpY2F0aXZlIGxhIGR1csOpZSBkZSBzw6lqb3VyLCBsYSB2YXJpYWJpbGl0w6kgaW5kaXZpZHVlbGxlDQpyZXN0YW50IGxhcmdlbWVudCBkb21pbmFudGUuDQoNCmBgYHtyfQ0KdGVtcHNfc3lzdGVtZTIgPC0gdGVtcHNfc3lzdGVtZTIgJT4lDQogIG11dGF0ZShwcmlvcml0YWlyZSA9IGZhY3Rvcihwcmlvcml0YWlyZSwgbGV2ZWxzID0gYygiT3VpIiwgIk5vbiIpKSkNCg0KdGVtcHNfc3lzdGVtZTINCmdncGxvdCh0ZW1wc19zeXN0ZW1lMiwgYWVzKHggPSBibG9jXzJoLCB5ID0gVywgY29sb3IgPSBwcmlvcml0YWlyZSkpICsNCiAgZ2VvbV9qaXR0ZXIod2lkdGggPSAwLjIsIGhlaWdodCA9IDAsIGFscGhhID0gMC41KSArDQogIGdlb21fcG9pbnQoYWVzKHkgPSBwcmVkX1cyKSwgc2hhcGUgPSAxNywgc2l6ZSA9IDMpICsNCiAgbGFicygNCiAgICB0aXRsZSA9ICJEdXLDqWUgZGUgc8Opam91ciBzZWxvbiBibG9jIDJoIGV0IHByaW9yaXTDqSIsDQogICAgeCA9ICJCbG9jIGhvcmFpcmUgZCdhcnJpdsOpZSIsDQogICAgeSA9ICJEdXLDqWUgZGUgc8Opam91ciBXIChoZXVyZXMpIiwNCiAgICBjb2xvciA9ICJQcmlvcml0YWlyZSINCiAgKSArDQogIHRoZW1lX21pbmltYWwoKQ0KDQoNCmBgYA0KDQpMZSBkZXJuaWVyIGdyYXBoaXF1ZSBtb250cmUgcXVlIGxhIGR1csOpZSBkZSBzw6lqb3VyIG1veWVubmUgZXN0IHRyw6hzDQpzaW1pbGFpcmUgcG91ciBsZXMgcGF0aWVudHMgcHJpb3JpdGFpcmVzIGV0IG5vbiBwcmlvcml0YWlyZXMsIHF1ZWwgcXVlDQpzb2l0IGxlIGJsb2MgaG9yYWlyZSBk4oCZYXJyaXbDqWUuIE9uIG9ic2VydmUgcXVlIGxhIGR1csOpZSBtb3llbm5lIHJlc3RlDQphdXRvdXIgZGUgMSBoIHBvdXIgbGUgbWF0aW4gKDho4oCTMTBoKSwgYXVnbWVudGUgbMOpZ8OocmVtZW50IHZlcnMgMSw1IGgNCnBvdXIgMTBo4oCTMTRoLCBwdWlzIGF0dGVpbnQgdW4gcGV1IHBsdXMgZGUgMiBoIHBvdXIgMTRo4oCTMTZoLCBhdmFudCBkZQ0KcmVkZXNjZW5kcmUgdmVycyAyIGggcG91ciAxNmjigJMxOGguIFF1ZWxxdWVzIHZhbGV1cnMgZXh0csOqbWVzDQphcHBhcmFpc3NlbnQsIGNvbW1lIHVuIHBhdGllbnQgcHJvY2hlIGRlIDYgaCBwb3VyIGxlIGJsb2MgMTRo4oCTMTZoLCBtYWlzDQplbGxlcyBuZSBtb2RpZmllbnQgcGFzIGxhIHRlbmRhbmNlIGfDqW7DqXJhbGUuIEdsb2JhbGVtZW50LCBsZXMgY291cmJlcw0KZGVzIHBhdGllbnRzIHByaW9yaXRhaXJlcyBldCBub24gcHJpb3JpdGFpcmVzIHNlIHN1cGVycG9zZW50LCBjZSBxdWkNCmNvbmZpcm1lIHF1ZSBsZSBzdGF0dXQgcHJpb3JpdGFpcmUgbuKAmWluZmx1ZW5jZSBwYXMgZGUgbWFuacOocmUNCnNpZ25pZmljYXRpdmUgbGUgdGVtcHMgcGFzc8OpIGRhbnMgbGUgc2VydmljZSwgZXQgcXVlIGxhIHZhcmlhYmlsaXTDqQ0KaW5kaXZpZHVlbGxlIHJlc3RlIGxhcmdlbWVudCBkb21pbmFudGUuDQoNCiMjIyBRdWVzdGlvbiA1KSBNb2TDqWxpc2F0aW9uIGRlIGxhIGR1csOpZSBkZSBzw6lqb3VyIHBhciB1biB0YXV4IGRlIGTDqXBhcnQgKC8zKQ0KDQpVbmUgbWFuacOocmUgYWx0ZXJuYXRpdmUgZGUgImfDqW7DqXJlciIgdW4gdGVtcHMgbGnDqSDDoCB1biDDqXbDqG5lbWVudCBlc3QNCmQndXRpbGlzZXIgbGUgdGF1eCBkZSBkw6lmYWlsbGFuY2UgZGUgc2EgZGlzdHJpYnV0aW9uIGTDqWZpbmkgcGFyIDoNCiQkXG11KHQpID0gXGxpbV97aCBcdG8gK1xpbmZ0eX0gXGZyYWN7XG1hdGhiYntQfShYPHQraHxYPnQpfXtofSA9IFxsaW1fe2ggXHRvICtcaW5mdHl9IFxmcmFje1xtYXRoYmJ7UH0oWDx0K2gpIC0gXG1hdGhiYntQfShYPHQpfXtcbWF0aGJie1B9KFg+dClofSA9IFxmcmFje2YodCl9ezEtRih0KX0gPSBcZnJhY3stXGZyYWN7ZFIodCl9e2R0fX17Uih0KX0gPSAtXGZyYWN7KGxuKFIodCkpfXtkdH0kJC4NCg0KRGFucyBsZSBjYXMgZGUgbGEgbG9pIGV4cG9uZW50aWVsLCBjZSB0YXV4IGVzdCBjb25zdGFudCBjYXINCiRcZnJhY3tmKHQpfXsxLUYodCl9ID0gXGxhbWJkYSBlXnstXGxhbWJkYSB0fSAvIChlXnstXGxhbWJkYSB0fSkgPSBcbGFtYmRhJCwNCmNlIHF1aSBlc3QgdW5lIGF1dHJlIG1hbmnDqHJlIGRlIHZvaXIgcXVlIGxhIGxhIGxvaSBlc3Qgc2FucyBtw6ltb2lyZS4NCg0KRGFucyBjZXR0ZSBxdWVzdGlvbiwgaWwgdm91cyBlc3QgYWluc2kgZGVtYW5kw6kgOg0KDQotICAgRXhwcmltZXIgKHNvdXMgZm9ybWUgZCfDqXF1YXRpb24gJFxtdSh0KT0uLi4kKSwgY2FsY3VsZXIgZXQgdHJhY2VyIGNlDQogICAgdGF1eCBwb3VyIGxhIGRpc3RyaWJ1dGlvbiBnYW1tYSBwb3VyIGxlcyAoMykgZGlzdHJpYnV0aW9ucyBtb2TDqWxpc2VyDQogICAgw6AgbGEgcXVlc3Rpb24gMyA6DQoNClLDqXBvbnNlIDoNCg0KUG91ciB1bmUgdmFyaWFibGUgYWzDqWF0b2lyZSAkWCQgc3VpdmFudCB1bmUgbG9pIEdhbW1hIGRlIHBhcmFtw6h0cmVzDQoqc2hhcGUqICRrJCBldCAqcmF0ZSogJFxsYW1iZGEkLCBsZSB0YXV4IGRlIGTDqWZhaWxsYW5jZSBlc3QgZMOpZmluaSBwYXIgOg0KDQokJA0KXG11KHQpID0gXGZyYWN7Zih0KX17MS1GKHQpfSA9IFxmcmFje1x0ZXh0e2RlbnNpdMOpIMOgIHR9fXtcdGV4dHtzdXJ2aWUgw6AgdH19LCBccXVhZCB0PjANCiQkDQoNCm/DuSA6DQoNCi0gICAkZih0KSA9IFxkZnJhY3tcbGFtYmRhXmt9e1xHYW1tYShrKX0gdF57ay0xfSBlXnstXGxhbWJkYSB0fSQgZXN0IGxhDQogICAgZGVuc2l0w6ksDQotICAgJEYodCkgPSBcaW50XzBedCBmKHMpIGRzJCBlc3QgbGEgZm9uY3Rpb24gZGUgcsOpcGFydGl0aW9uLA0KLSAgICRTKHQpID0gMSAtIEYodCkkIGVzdCBsYSBmb25jdGlvbiBkZSBzdXJ2aWUuDQoNCkZvcm11bGVzIHBvdXIgbGVzIGRpc3RyaWJ1dGlvbnMgYWp1c3TDqWVzIDoNCg0KUGF0aWVudHMgcHJpb3JpdGFpcmVzIDoNCg0KJCQNClxtdV97XHRleHR7cHJpb3JpdGFpcmVzfX0odCkgPQ0KXGRmcmFjew0KXGZyYWN7MS44NTdeezEuODZ9fXtcR2FtbWEoMS44Nil9IHReezAuODZ9IGVeey0xLjA1NyB0fQ0KfXsNCjEgLSBcaW50XzBedCBcZnJhY3sxLjg1N157MS44Nn19e1xHYW1tYSgxLjg2KX0gc157MC44Nn0gZV57LTEuMDU3IHN9IGRzDQp9LCBccXVhZCB0PjANCiQkDQoNClBhdGllbnRzIG5vbiBwcmlvcml0YWlyZXMgOg0KDQokJA0KXG11X3tcdGV4dHtub24tcHJpb3JpdGFpcmVzfX0odCkgPQ0KXGRmcmFjew0KXGZyYWN7MS44OTJeezMuMjU3fX17XEdhbW1hKDMuMjU3KX0gdF57Mi4yNTd9IGVeey0xLjg5MiB0fQ0KfXsNCjEgLSBcaW50XzBedCBcZnJhY3sxLjg5Ml57My4yNTd9fXtcR2FtbWEoMy4yNTcpfSBzXnsyLjI1N30gZV57LTEuODkyIHN9IGRzDQp9LCBccXVhZCB0PjANCiQkDQoNClRvdXMgbGVzIHBhdGllbnRzIDoNCg0KJCQNClxtdV97XHRleHR7dG91c319KHQpID0NClxkZnJhY3sNClxmcmFjezEuNV57Mi41fX17XEdhbW1hKDIuNSl9IHReezEuNX0gZV57LTEuNSB0fQ0KfXsNCjEgLSBcaW50XzBedCBcZnJhY3sxLjVeezIuNX19e1xHYW1tYSgyLjUpfSBzXnsxLjV9IGVeey0xLjUgc30gZHMNCn0sIFxxdWFkIHQ+MA0KJCQNCg0KYGBge3J9DQojX0VudHJleiB2b3RyZSBjb2RlIFIgaWNpXw0KbGlicmFyeShNQVNTKQ0KDQojIFPDqXBhcmVyIGxlcyBkdXLDqWVzIHBhciBwcmlvcml0w6kNCmR1cmVlX3ByaW8gPC0gZGF0YV9wYXRpZW50ICU+JQ0KICBmaWx0ZXIocHJpb3JpdGFpcmUgPT0gIk91aSIpICU+JQ0KICBwdWxsKGR1cmVlX3Nlam91cikNCg0KZHVyZWVfbm9uX3ByaW8gPC0gZGF0YV9wYXRpZW50ICU+JQ0KICBmaWx0ZXIocHJpb3JpdGFpcmUgPT0gIk5vbiIpICU+JQ0KICBwdWxsKGR1cmVlX3Nlam91cikNCg0KIyBBanVzdGVtZW50IEdhbW1hIHBvdXIgY2hhcXVlIGdyb3VwZQ0KZml0X2dhbW1hX2FsbCAgICAgICA8LSBmaXRkaXN0cihkdXJlZSwgImdhbW1hIikNCmZpdF9nYW1tYV9wcmlvICAgICAgPC0gZml0ZGlzdHIoZHVyZWVfcHJpbywgImdhbW1hIikNCmZpdF9nYW1tYV9ub25fcHJpbyAgPC0gZml0ZGlzdHIoZHVyZWVfbm9uX3ByaW8sICJnYW1tYSIpDQoNCiMgUsOpY3Vww6lyYXRpb24gZGVzIHBhcmFtw6h0cmVzIHNoYXBlIGV0IHJhdGUNCnNoYXBlX2FsbCAgPC0gZml0X2dhbW1hX2FsbCRlc3RpbWF0ZVsic2hhcGUiXQ0KcmF0ZV9hbGwgICA8LSBmaXRfZ2FtbWFfYWxsJGVzdGltYXRlWyJyYXRlIl0NCg0Kc2hhcGVfcHJpbyA8LSBmaXRfZ2FtbWFfcHJpbyRlc3RpbWF0ZVsic2hhcGUiXQ0KcmF0ZV9wcmlvICA8LSBmaXRfZ2FtbWFfcHJpbyRlc3RpbWF0ZVsicmF0ZSJdDQoNCnNoYXBlX25vbl9wcmlvIDwtIGZpdF9nYW1tYV9ub25fcHJpbyRlc3RpbWF0ZVsic2hhcGUiXQ0KcmF0ZV9ub25fcHJpbyAgPC0gZml0X2dhbW1hX25vbl9wcmlvJGVzdGltYXRlWyJyYXRlIl0NCg0KIyBBZmZpY2hlciBsZXMgcGFyYW3DqHRyZXMNCmNhdCgiPT09IFBBUkFNw4hUUkVTIERFUyBMT0lTIEdBTU1BID09PVxuIikNCmNhdCgiVG91cyBsZXMgcGF0aWVudHMgOiBzaGFwZSA9Iiwgcm91bmQoc2hhcGVfYWxsLCAzKSwgIiwgcmF0ZSA9Iiwgcm91bmQocmF0ZV9hbGwsIDMpLCAiXG4iKQ0KY2F0KCJQcmlvcml0YWlyZXMgOiBzaGFwZSA9Iiwgcm91bmQoc2hhcGVfcHJpbywgMyksICIsIHJhdGUgPSIsIHJvdW5kKHJhdGVfcHJpbywgMyksICJcbiIpDQpjYXQoIk5vbiBwcmlvcml0YWlyZXMgOiBzaGFwZSA9Iiwgcm91bmQoc2hhcGVfbm9uX3ByaW8sIDMpLCAiLCByYXRlID0iLCByb3VuZChyYXRlX25vbl9wcmlvLCAzKSwgIlxuXG4iKQ0KDQojIETDqWZpbml0aW9uIGRlIGxhIGZvbmN0aW9uIGhhemFyZCBwb3VyIGxhIEdhbW1hDQpoYXphcmRfZ2FtbWEgPC0gZnVuY3Rpb24odCwgc2hhcGUsIHJhdGUpIHsNCiAgZiA8LSBkZ2FtbWEodCwgc2hhcGU9c2hhcGUsIHJhdGU9cmF0ZSkgICAgICAgICAgIyBkZW5zaXTDqSBmKHQpDQogIFMgPC0gMSAtIHBnYW1tYSh0LCBzaGFwZT1zaGFwZSwgcmF0ZT1yYXRlKSAgICAgICMgc3VydmllIFModCkgPSAxLUYodCkNCiAgcmV0dXJuKGYgLyBTKSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIM68KHQpID0gZih0KS9TKHQpDQp9DQoNCiMgVmVjdGV1ciBkZSB0ZW1wcyBwb3VyIGxlIGNhbGN1bA0KdF92YWxzIDwtIHNlcSgwLjAxLCAxMCwgYnk9MC4wMSkgICMgw6l2aXRlciB0PTAgcG91ciBsYSBHYW1tYQ0KDQojIENhbGN1bCBkZXMgaGF6YXJkIHBvdXIgY2hhcXVlIGdyb3VwZQ0KaGF6X2FsbCAgICAgICA8LSBoYXphcmRfZ2FtbWEodF92YWxzLCBzaGFwZV9hbGwsIHJhdGVfYWxsKQ0KaGF6X3ByaW8gICAgICA8LSBoYXphcmRfZ2FtbWEodF92YWxzLCBzaGFwZV9wcmlvLCByYXRlX3ByaW8pDQpoYXpfbm9uX3ByaW8gIDwtIGhhemFyZF9nYW1tYSh0X3ZhbHMsIHNoYXBlX25vbl9wcmlvLCByYXRlX25vbl9wcmlvKQ0KDQojIFRyYWPDqSBkdSB0YXV4IGRlIGTDqWZhaWxsYW5jZQ0KcGxvdCh0X3ZhbHMsIGhhel9hbGwsIHR5cGU9ImwiLCBjb2w9ImJsYWNrIiwgbHdkPTIsDQogICAgIHlsYWI9IlRhdXggZGUgZMOpZmFpbGxhbmNlIM68KHQpIiwgeGxhYj0iVGVtcHMgdCAoaGV1cmVzKSIsDQogICAgIG1haW49IlRhdXggZGUgZMOpZmFpbGxhbmNlIC0gbG9pIEdhbW1hIiwNCiAgICAgeWxpbT1jKDAsIG1heChjKGhhel9hbGwsIGhhel9wcmlvLCBoYXpfbm9uX3ByaW8pLCBuYS5ybT1UUlVFKSkpDQpsaW5lcyh0X3ZhbHMsIGhhel9wcmlvLCBjb2w9InJlZCIsIGx3ZD0yKQ0KbGluZXModF92YWxzLCBoYXpfbm9uX3ByaW8sIGNvbD0iYmx1ZSIsIGx3ZD0yKQ0KbGVnZW5kKCJ0b3ByaWdodCIsIA0KICAgICAgIGxlZ2VuZD1jKCJUb3VzIiwgIlByaW9yaXRhaXJlcyIsICJOb24gcHJpb3JpdGFpcmVzIiksDQogICAgICAgY29sPWMoImJsYWNrIiwicmVkIiwiYmx1ZSIpLCBsd2Q9MikNCg0KDQpgYGANCg0KKipBbmFseXNlIGR1IHRhdXggZGUgZMOpZmFpbGxhbmNlIDoqKg0KDQpM4oCZYW5hbHlzZSBkZXMgcGFyYW3DqHRyZXMgZGVzIGxvaXMgR2FtbWEgbW9udHJlIHF1ZSBsZSB0YXV4IGRlDQpkw6lmYWlsbGFuY2UgYXVnbWVudGUgYXZlYyBsZSB0ZW1wcyBwb3VyIHRvdXMgbGVzIGdyb3VwZXMsIHB1aXNxdWUgbGVzDQpwYXJhbcOodHJlcyBkZSBmb3JtZSBzb250IHN1cMOpcmlldXJzIMOgIDEuIExlcyBwYXRpZW50cyBwcmlvcml0YWlyZXMNCnByw6lzZW50ZW50IHVuIHBhcmFtw6h0cmUgZGUgZm9ybWUgcGx1cyBmYWlibGUgKGsgPSAxLDg2KSwgY2UgcXVpIHNlDQp0cmFkdWl0IHBhciB1bmUgYXVnbWVudGF0aW9uIHByb2dyZXNzaXZlIGR1IHRhdXggZGUgZMOpZmFpbGxhbmNlIGF1IGNvdXJzDQpkdSB0ZW1wcywgaW5kaXF1YW50IHF1ZSBsZXVyIHJpc3F1ZSBkZSBjb21wbGljYXRpb24gb3UgZOKAmcOpdsOpbmVtZW50DQphZHZlcnNlIGNyb8OudCBsZW50ZW1lbnQuIExlcyBwYXRpZW50cyBub24gcHJpb3JpdGFpcmVzLCBlbiByZXZhbmNoZSwgb250DQp1biBwYXJhbcOodHJlIGRlIGZvcm1lIHBsdXMgw6lsZXbDqSAoayA9IDMsMjYpLCBjb3JyZXNwb25kYW50IMOgIHVuIHRhdXggZGUNCmTDqWZhaWxsYW5jZSBxdWkgY3Jvw650IHJhcGlkZW1lbnQsIGRvbmMgbGV1ciByaXNxdWUgYXVnbWVudGUgZm9ydGVtZW50DQphdmVjIGxhIGR1csOpZSBwYXNzw6llIGRhbnMgbGUgc2VydmljZS4gTOKAmWVuc2VtYmxlIGRlcyBwYXRpZW50cyBtb250cmUgdW4NCmNvbXBvcnRlbWVudCBpbnRlcm3DqWRpYWlyZSAoayA9IDIsNjgpLCByZWZsw6l0YW50IGxhIGNvbWJpbmFpc29uIGRlcyBkZXV4DQpkeW5hbWlxdWVzLiBFbiBjb25jbHVzaW9uLCBs4oCZYW5hbHlzZSBkdSB0YXV4IGRlIGTDqWZhaWxsYW5jZSBtb250cmUgcXVlDQpsZXMgcGF0aWVudHMgbm9uIHByaW9yaXRhaXJlcyBwcsOpc2VudGVudCB1biByaXNxdWUgcXVpIGF1Z21lbnRlIHBsdXMNCnJhcGlkZW1lbnQgYXZlYyBsZSB0ZW1wcyBxdWUgY2VsdWkgZGVzIHBhdGllbnRzIHByaW9yaXRhaXJlcy4gQXV0cmVtZW50DQpkaXQsIHBsdXMgdW4gcGF0aWVudCBub24gcHJpb3JpdGFpcmUgcmVzdGUgZGFucyBsZSBzZXJ2aWNlLCBwbHVzIGlsIGEgZGUNCmNoYW5jZXMgZGUgcmVuY29udHJlciB1biBwcm9ibMOobWUgb3UgdW5lIGNvbXBsaWNhdGlvbiwgdGFuZGlzIHF1ZSBjZQ0KcmlzcXVlIGNyb8OudCBwbHVzIGxlbnRlbWVudCBwb3VyIGxlcyBwYXRpZW50cyBwcmlvcml0YWlyZXMuIEzigJllbnNlbWJsZQ0KZGVzIHBhdGllbnRzIHNlIHNpdHVlIGVudHJlIGNlcyBkZXV4IGR5bmFtaXF1ZXMuDQoNCi0gICBEZSBjb25zdHJ1aXJlIGV0IGQnYW5hbHlzZXIgdW4gbW9kw6hsZSAoY2FsY3VsIGRlIG1veWVubmVzIHNpbXBsZXMpDQogICAgcXVpIGVzdGltZSBjZSB0YXV4IGRlIHJpc3F1ZSBwYXIgdHJhbmNoZSBkZSAzMG1pbg0KICAgIChbMDszMG1pbl0sXTMwbWluOzYwbWluXS4uLikgcG91ciBsJ2Vuc2VtYmxlIGRlcyBwYXRpZW50cywgcHVpcw0KICAgIHPDqXBhcmFudCBzZWxvbiBsZSBibG9jIGQnYXJyaXbDqWUgZGUgMmggWzhoOzEwaF0sIC4uLiwgXTE2aDsxOGhdLA0KICAgIHB1aXMgZW4gc8OpcGFyYW50IHBhciBwYXRpZW50cyBwcmlvcml0YWlyZXMgZXQgbm9uIHByaW9yaXRhaXJlcw0KDQoqRW50cmV6IHZvdHJlIHRleHRlIGV0IGZvcm11bGVzIGljaSoNCg0KYGBge3J9DQojX0VudHJleiB2b3RyZSBjb2RlIFIgaWNpXw0KDQpsaWJyYXJ5KGdncGxvdDIpDQpsaWJyYXJ5KGRwbHlyKQ0KbGlicmFyeShsdWJyaWRhdGUpDQoNCmJyZWFrc18zMCA8LSBzZXEoMCwgY2VpbGluZyhtYXgoZGF0YV9wYXRpZW50JGR1cmVlX3Nlam91cikpLCBieSA9IDAuNSkNCg0KaGF6YXJkX3Bhcl90cmFuY2hlIDwtIGZ1bmN0aW9uKGR1cmVlcywgYnJlYWtzKSB7DQogIG4gPC0gbGVuZ3RoKGJyZWFrcykgLSAxDQogIGhhemFyZCA8LSBudW1lcmljKG4pDQogIA0KICBmb3IgKGkgaW4gMTpuKSB7DQogICAgZGVidXQgPC0gYnJlYWtzW2ldDQogICAgZmluICAgPC0gYnJlYWtzW2kgKyAxXQ0KICAgIA0KICAgIGFfcmlzcXVlIDwtIHN1bShkdXJlZXMgPj0gZGVidXQpDQogICAgc29ydGllcyAgPC0gc3VtKGR1cmVlcyA+PSBkZWJ1dCAmIGR1cmVlcyA8IGZpbikNCiAgICANCiAgICBoYXphcmRbaV0gPC0gaWZlbHNlKGFfcmlzcXVlID4gMCwgc29ydGllcyAvIGFfcmlzcXVlLCBOQSkNCiAgfQ0KICANCiAgZGF0YS5mcmFtZSgNCiAgICBkZWJ1dCA9IGJyZWFrc1stbGVuZ3RoKGJyZWFrcyldLA0KICAgIGZpbiAgID0gYnJlYWtzWy0xXSwNCiAgICBoYXphcmQgPSBoYXphcmQNCiAgKQ0KfQ0KDQpoYXphcmRfZ2xvYmFsIDwtIGhhemFyZF9wYXJfdHJhbmNoZShkYXRhX3BhdGllbnQkZHVyZWVfc2Vqb3VyLCBicmVha3NfMzApDQoNCmdncGxvdChoYXphcmRfZ2xvYmFsLCBhZXMoeCA9IGRlYnV0LCB5ID0gaGF6YXJkKSkgKw0KICBnZW9tX2xpbmUobGluZXdpZHRoID0gMSwgY29sb3IgPSAiYmxhY2siKSArDQogIGdlb21fcG9pbnQoc2l6ZSA9IDIsIGNvbG9yID0gImJsYWNrIikgKw0KICBsYWJzKA0KICAgIHRpdGxlID0gIlRhdXggZGUgZMOpZmFpbGxhbmNlIGVtcGlyaXF1ZSDigJMgVG91cyBwYXRpZW50cyAodHJhbmNoZXMgZGUgMzBtaW4pIiwNCiAgICB4ID0gIlRlbXBzIGRlIHPDqWpvdXIgKGhldXJlcykiLA0KICAgIHkgPSAiVGF1eCBkZSBkw6lmYWlsbGFuY2UiDQogICkgKw0KICB0aGVtZV9taW5pbWFsKCkNCg0KZGF0YV9wYXRpZW50IDwtIGRhdGFfcGF0aWVudCAlPiUNCiAgbXV0YXRlKA0KICAgIGhldXJlX2Fycml2ZWUgPSBob3VyKGFycml2YWxfdGltZSksDQogICAgYmxvY18yaCA9IGN1dCgNCiAgICAgIGhldXJlX2Fycml2ZWUsDQogICAgICBicmVha3MgPSBjKDgsIDEwLCAxMiwgMTQsIDE2LCAxOCksDQogICAgICByaWdodCA9IEZBTFNFLA0KICAgICAgaW5jbHVkZS5sb3dlc3QgPSBUUlVFLA0KICAgICAgbGFiZWxzID0gYygiWzhoLTEwaFsiLCAiWzEwaC0xMmhbIiwgIlsxMmgtMTRoWyIsICJbMTRoLTE2aFsiLCAiWzE2aC0xOGhbIikNCiAgICApDQogICkNCg0KaGF6YXJkX3Bhcl9ibG9jIDwtIGRhdGFfcGF0aWVudCAlPiUNCiAgZ3JvdXBfYnkoYmxvY18yaCkgJT4lDQogIGdyb3VwX21hcCh+IGhhemFyZF9wYXJfdHJhbmNoZSgueCRkdXJlZV9zZWpvdXIsIGJyZWFrc18zMCksIC5rZWVwID0gVFJVRSkNCg0KaGF6YXJkX2Jsb2NfZGYgPC0gYmluZF9yb3dzKGhhemFyZF9wYXJfYmxvYywgLmlkID0gImJsb2NfaWQiKQ0KaGF6YXJkX2Jsb2NfZGYkYmxvYyA8LSBsZXZlbHMoZGF0YV9wYXRpZW50JGJsb2NfMmgpW2FzLm51bWVyaWMoaGF6YXJkX2Jsb2NfZGYkYmxvY19pZCldDQoNCmdncGxvdChoYXphcmRfYmxvY19kZiwgYWVzKHggPSBkZWJ1dCwgeSA9IGhhemFyZCwgY29sb3IgPSBibG9jKSkgKw0KICBnZW9tX2xpbmUobGluZXdpZHRoID0gMS4yKSArDQogIGxhYnMoDQogICAgdGl0bGUgPSAiVGF1eCBkZSBkw6lmYWlsbGFuY2UgcGFyIGJsb2MgZCdhcnJpdsOpZSAoMmgpIiwNCiAgICB4ID0gIlRlbXBzIGRlIHPDqWpvdXIgKGhldXJlcykiLA0KICAgIHkgPSAiVGF1eCBkZSBkw6lmYWlsbGFuY2UiLA0KICAgIGNvbG9yID0gIkJsb2MgZCdhcnJpdsOpZSINCiAgKSArDQogIHRoZW1lX21pbmltYWwoKSArDQogIHNjYWxlX2NvbG9yX2JyZXdlcihwYWxldHRlID0gIlNldDEiKQ0KDQpoYXphcmRfcHJpb3JpdGUgPC0gZGF0YV9wYXRpZW50ICU+JQ0KICBncm91cF9ieShwcmlvcml0YWlyZSkgJT4lDQogIGdyb3VwX21hcCh+IGhhemFyZF9wYXJfdHJhbmNoZSgueCRkdXJlZV9zZWpvdXIsIGJyZWFrc18zMCksIC5rZWVwID0gVFJVRSkNCg0KaGF6YXJkX3ByaW9yX2RmIDwtIGJpbmRfcm93cyhoYXphcmRfcHJpb3JpdGUsIC5pZCA9ICJwcmlvcml0YWlyZV9pZCIpDQojIEFqdXN0ZW1lbnQgZGVzIGxhYmVscyBzZWxvbiBsYSB2YXJpYWJsZSBwcmlvcml0YWlyZQ0KcHJpb3JfbGFiZWxzIDwtIGxldmVscyhmYWN0b3IoZGF0YV9wYXRpZW50JHByaW9yaXRhaXJlKSkNCmhhemFyZF9wcmlvcl9kZiRwcmlvcml0YWlyZSA8LSBwcmlvcl9sYWJlbHNbYXMubnVtZXJpYyhoYXphcmRfcHJpb3JfZGYkcHJpb3JpdGFpcmVfaWQpXQ0KDQpnZ3Bsb3QoaGF6YXJkX3ByaW9yX2RmLCBhZXMoeCA9IGRlYnV0LCB5ID0gaGF6YXJkLCBjb2xvciA9IHByaW9yaXRhaXJlKSkgKw0KICBnZW9tX2xpbmUobGluZXdpZHRoID0gMS41KSArDQogIGxhYnMoDQogICAgdGl0bGUgPSAiVGF1eCBkZSBkw6lmYWlsbGFuY2Ug4oCTIFBhdGllbnRzIHByaW9yaXRhaXJlcyB2cyBub24gcHJpb3JpdGFpcmVzICgzMG1pbikiLA0KICAgIHggPSAiVGVtcHMgZGUgc8Opam91ciAoaGV1cmVzKSIsDQogICAgeSA9ICJUYXV4IGRlIGTDqWZhaWxsYW5jZSIsDQogICAgY29sb3IgPSAiUHJpb3JpdGFpcmUiDQogICkgKw0KICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYygiT3VpIiA9ICJyZWQiLCAiTm9uIiA9ICJibHVlIikpICsNCiAgdGhlbWVfbWluaW1hbCgpDQoNCmBgYA0KDQpJbnRlcnByw6l0YXRpb24gZGVzIHRhdXggZGUgcmlzcXVlIHBhciB0cmFuY2hlcyA6DQoNCkxlIHRhdXggZGUgZMOpZmFpbGxhbmNlIHBhciB0cmFuY2hlcyBkZSAzMCBtaW51dGVzIG1vbnRyZSBxdWUgbGUgcmlzcXVlDQp2YXJpZSBmb3J0ZW1lbnQgYXUgY291cnMgZHUgc8Opam91ci4gQXUgZMOpYnV0LCBpbCBlc3QgcmVsYXRpdmVtZW50IGZhaWJsZQ0KKFw8MCwzKSBwZW5kYW50IGxhIHByZW1pw6hyZSBoZXVyZSwgcHVpcyBpbCBhdWdtZW50ZSBwb3VyIGF0dGVpbmRyZSB1bg0KcHJlbWllciBwaWMgZGUgMCw1IGF1dG91ciBkZSAxaDMwLCByZWRlc2NlbmQgw6AgMCwyLCBhdmFudCBkZSByZW1vbnRlciDDoA0KMCw2NSwgZXQgYWluc2kgZGUgc3VpdGUuIENldHRlIGR5bmFtaXF1ZSBpbGx1c3RyZSBxdWUgY2VydGFpbnMgcGF0aWVudHMNCnNvcnRlbnQgcGx1cyByYXBpZGVtZW50IHF1ZSBk4oCZYXV0cmVzLCBjcsOpYW50IGRlcyB2YXJpYXRpb25zIGR1IHJpc3F1ZQ0KZGFucyBsZSB0ZW1wcy4gRW4gY29tcGFyYW50IGxlcyBncm91cGVzLCBsZXMgcGF0aWVudHMgbm9uIHByaW9yaXRhaXJlcw0KcHLDqXNlbnRlbnQgZGVzIHBpY3MgcGx1cyByYXBpZGVzIGV0IMOpbGV2w6lzIChqdXNxdeKAmcOgIDEpLCB0YW5kaXMgcXVlIGxlcw0KcGF0aWVudHMgcHJpb3JpdGFpcmVzIG9udCB1bmUgYXVnbWVudGF0aW9uIHBsdXMgcHJvZ3Jlc3NpdmUgZHUgdGF1eCBkZQ0KZMOpZmFpbGxhbmNlIChwaWNzIGF1dG91ciBkZSAwLDUpLCByZWZsw6l0YW50IHVuZSBwcmlzZSBlbiBjaGFyZ2UgcGx1cw0KcsOpZ3VsacOocmUuIExlcyBibG9jcyBob3JhaXJlcyBwbHVzIGNoYXJnw6lzIGFjY2VudHVlbnQgw6lnYWxlbWVudCBsZXMgcGljcw0KZGUgcmlzcXVlLCBtb250cmFudCBxdWUgbGEgY29uZ2VzdGlvbiBpbmZsdWVuY2UgbGEgcHJvYmFiaWxpdMOpDQpk4oCZw6l2w6luZW1lbnQuIEFpbnNpLCBsZSBzdWl2aSBkdSB0YXV4IGRlIGTDqWZhaWxsYW5jZSBwYXIgdHJhbmNoZSBwZXJtZXQNCmRlIG1ldHRyZSBlbiDDqXZpZGVuY2UgbGVzIHDDqXJpb2RlcyBldCBsZXMgcG9wdWxhdGlvbnMgw6AgcGx1cyBoYXV0DQpyaXNxdWUsIGluZm9ybWF0aW9uIHF14oCZdW5lIG1veWVubmUgZ2xvYmFsZSBuZSBwb3VycmFpdCBwYXMgcsOpdsOpbGVyLg0KDQpMZSBkZXV4acOobWUgZ3JhcGhpcXVlIG1vbnRyZSBsZSB0YXV4IGRlIGTDqWZhaWxsYW5jZSBlbiBmb25jdGlvbiBkdSBibG9jDQpob3JhaXJlIGTigJlhcnJpdsOpZSBkZXMgcGF0aWVudHMuIE9uIG9ic2VydmUgcXVlIGzigJloZXVyZSBk4oCZYXJyaXbDqWUNCmluZmx1ZW5jZSBsZSByaXNxdWUgYXUgY291cnMgZHUgc8Opam91ci4gUGFyIGV4ZW1wbGUsIGxlcyBwYXRpZW50cw0KYXJyaXZhbnQgZW50cmUgOGggZXQgMTBoIHByw6lzZW50ZW50IHVuIHRhdXggZGUgZMOpZmFpbGxhbmNlIGluZsOpcmlldXIgw6ANCjAsNSBwZW5kYW50IGxhIHByZW1pw6hyZSBoZXVyZSwgcHVpcyBjZWx1aS1jaSBhdWdtZW50ZSByYXBpZGVtZW50IHBvdXINCmF0dGVpbmRyZSAxIGF1IGJvdXQgZGUgMiBoZXVyZXMsIGluZGlxdWFudCB1bmUgc29ydGllIG91IHVuIMOpdsOpbmVtZW50DQpwbHVzIGNvbmNlbnRyw6kgZGFucyBsZSB0ZW1wcy4gRW4gcmV2YW5jaGUsIGxlcyBwYXRpZW50cyBhcnJpdmFudCBlbnRyZQ0KMTRoIGV0IDE2aCBwcsOpc2VudGVudCBkZXMgdmFyaWF0aW9ucyBwbHVzIGdyYWR1ZWxsZXMgOiBsZSB0YXV4IHJlc3RlIGVuDQpkZXNzb3VzIGRlIDAsNSBwZW5kYW50IGVudmlyb24gNSBoZXVyZXMgYXZhbnQgZOKAmWF0dGVpbmRyZSAxIGFwcsOocyA2DQpoZXVyZXMsIHJlZmzDqXRhbnQgdW5lIHByw6lzZW5jZSBwbHVzIGxvbmd1ZSBkYW5zIGxlIHN5c3TDqG1lLiBDZXMNCm9ic2VydmF0aW9ucyBzdWdnw6hyZW50IHF1ZSBsZXMgcMOpcmlvZGVzIGRlIGZvcnRlIGFmZmx1ZW5jZSBvdSBsZXMgYmxvY3MNCmhvcmFpcmVzIHBsdXMgY2hhcmfDqXMgcGV1dmVudCBhY2NlbnR1ZXIgbGUgcmlzcXVlIGRlIGTDqWZhaWxsYW5jZSwNCnNvdWxpZ25hbnQgbOKAmWltcGFjdCBkZSBs4oCZb3JnYW5pc2F0aW9uIGV0IGRlIGxhIGNvbmdlc3Rpb24gc3VyIGxhDQpkeW5hbWlxdWUgZHUgc8Opam91ci4NCg0KRW5maW4sIGxlIGRlcm5pZXIgZ3JhcGhpcXVlIG1vbnRyZSBs4oCZw6l2b2x1dGlvbiBkdSB0YXV4IGRlIGTDqWZhaWxsYW5jZQ0Kc2Vsb24gbGUgY2FyYWN0w6hyZSBwcmlvcml0YWlyZSBvdSBub24gZGVzIHBhdGllbnRzLiBPbiBvYnNlcnZlIHF1ZSwNCmdsb2JhbGVtZW50LCBsZSB0YXV4IGRlIGTDqWZhaWxsYW5jZSBkZXMgcGF0aWVudHMgcHJpb3JpdGFpcmVzIHJlc3RlDQppbmbDqXJpZXVyIMOgIGNlbHVpIGRlcyBwYXRpZW50cyBub24gcHJpb3JpdGFpcmVzIHBlbmRhbnQgbGVzIHF1YXRyZQ0KcHJlbWnDqHJlcyBoZXVyZXMsIHJlZmzDqXRhbnQgdW5lIHByaXNlIGVuIGNoYXJnZSBwbHVzIHJhcGlkZSBldA0KcsOpZ3VsacOocmUuIEVuIHJldmFuY2hlLCBwb3VyIGxlcyBwYXRpZW50cyBub24gcHJpb3JpdGFpcmVzLCBsZSB0YXV4IGRlDQpkw6lmYWlsbGFuY2UgYXR0ZWludCByYXBpZGVtZW50IDEsIGluZGlxdWFudCBxdWUgbGV1ciByaXNxdWUgZOKAmcOpdsOpbmVtZW50DQphdWdtZW50ZSBmb3J0ZW1lbnQgYXByw6hzIHVuIGNlcnRhaW4gdGVtcHMgZOKAmWF0dGVudGUsIHRhbmRpcyBxdWUgcG91ciBsZXMNCnBhdGllbnRzIHByaW9yaXRhaXJlcywgbGUgdGF1eCBu4oCZYXR0ZWludCAxIHF14oCZYXUgYm91dCBkZSA1aDMwLCBtb250cmFudA0KcXVlIGxlIHJpc3F1ZSBjcm/DrnQgcGx1cyBsZW50ZW1lbnQuIENldHRlIGRpZmbDqXJlbmNlIHNvdWxpZ25lIGzigJllZmZldA0KcHJvdGVjdGV1ciBkZSBsYSBwcmlvcmlzYXRpb24gZXQgbWV0IGVuIMOpdmlkZW5jZSBs4oCZaW1wb3J0YW5jZSBkZSBsYQ0KZ2VzdGlvbiBkdSBmbHV4IGRlcyBwYXRpZW50cyBwb3VyIHLDqWR1aXJlIGxlIHJpc3F1ZSBkZSBjb21wbGljYXRpb25zIG91DQpk4oCZw6l2w6luZW1lbnRzIGluZMOpc2lyYWJsZXMuDQoNCiMjIyBRdWVzdGlvbiA2KSBDYWxjdWxzIGRlcyBuaXZlYXV4IGQnb2NjdXBhdGlvbiBldCB2YWxpZGF0aW9uIGRlcyBtb2TDqGxlcyAoLzUpDQoNCkEgcGFydGlyIGRlcyBtb2TDqGxlcyBwcsOpY8OpZGFudCwgaWwgdm91cyBlc3QgZGVtYW5kw6kgZCdlc3RpbWVyLCBkZQ0KdmlzdWFsaXNlciBldCBkJ2FuYWx5c2VyIGxlIG5pdmVhdSBkJ29jY3VwYXRpb24gYXUgY291cnMgZGUgbGEgam91cm7DqWUNCmVuIHV0aWxpc2FudCAzMCDDqWNoYW50aWxsb25zIGRlIHByb2Nlc3N1cyBkJ2Fycml2w6llIGRlIHBhdGllbnQgKDMwDQpqb3VybsOpZXMgZXQgcGFzIDMwIHBhdGllbnRzKSBJbCB2b3VzIGVzdCBlbnN1aXRlIGRlbWFuZMOpIGRlIGNvbXBhcmVyIGxhDQpxdWFsaXTDqSBkZXMgZXN0aW1hdGlvbnMgZGVzIGRpZmbDqXJlbnRzIG1vZMOobGVzIMOgIGxhIHLDqWFsaXTDqSBkZSBtYW5pw6hyZQ0KdmlzdWVsbGUgZXQgZW4gdXRpbGlzYW50IHVuZSBtZXN1cmUgZCdlcnJldXIgYWJzb2x1IG1veWVubmUgZXQvb3UNCnF1YWRyYXRpcXVlIChvdSBtaXMgw6AgbGEgJFxzcXJ0KCkkKSBldCB1bmUgbWVzdXJlIGRlIGJpYWlzIHN1cg0KbCdvY2N1cGF0aW9uIG1veWVubmUuDQoNClLDqXBvbnNlIDoNCg0KKkVudHJleiB2b3RyZSB0ZXh0ZSBpY2kqDQoNCmBgYHtyfQ0KI19FbnRyZXogdm90cmUgY29kZSBSIGljaV8NCg0KIyBGb25jdGlvbiBwb3VyIGNhbGN1bGVyIGwnb2NjdXBhdGlvbiByw6llbGxlDQpjYWxjdWxlcl9vY2N1cGF0aW9uX3JlZWxsZSA8LSBmdW5jdGlvbihkYXRhX3BhdGllbnQsIGRlYnV0LCBmaW4sIHBhc190ZW1wcyA9IDAuMSkgew0KICB0ZW1wc19zZXEgPC0gc2VxKGRlYnV0LCBmaW4sIGJ5ID0gcGFzX3RlbXBzICogMzYwMCkNCiAgb2NjdXBhdGlvbiA8LSBzYXBwbHkodGVtcHNfc2VxLCBmdW5jdGlvbih0KSB7DQogICAgc3VtKGRhdGFfcGF0aWVudCRhcnJpdmFsX3RpbWUgPD0gdCAmIGRhdGFfcGF0aWVudCRkZXBhcnR1cmVfdGltZSA+PSB0KQ0KICB9KQ0KICBkYXRhLmZyYW1lKHRlbXBzID0gdGVtcHNfc2VxLCBvY2N1cGF0aW9uID0gb2NjdXBhdGlvbikNCn0NCg0KIyBEw6lmaW5pciBsYSBww6lyaW9kZSBkJ29ic2VydmF0aW9uDQpkYXRlX2pvdXIgPC0gYXMuRGF0ZSgiMjAxNS0xMS0xMiIpDQpkZWJ1dF9qb3VybmVlIDwtIHltZF9obXMocGFzdGUoZGF0ZV9qb3VyLCAiMDg6MDA6MDAiKSkNCmZpbl9qb3VybmVlIDwtIHltZF9obXMocGFzdGUoZGF0ZV9qb3VyLCAiMTg6MDA6MDAiKSkNCg0KIyBDYWxjdWxlciBsJ29jY3VwYXRpb24gcsOpZWxsZQ0Kb2NjdXBhdGlvbl9yZWVsbGUgPC0gY2FsY3VsZXJfb2NjdXBhdGlvbl9yZWVsbGUoZGF0YV9wYXRpZW50LCBkZWJ1dF9qb3VybmVlLCBmaW5fam91cm5lZSkNCg0KIyBUYXV4IGQnYXJyaXbDqWUgcGFyIGJsb2MgZGUgMmgNCmRhdGFfcGF0aWVudCA8LSBkYXRhX3BhdGllbnQgJT4lDQogIG11dGF0ZSgNCiAgICBoZXVyZV9hcnJpdmVlID0gaG91cihhcnJpdmFsX3RpbWUpLA0KICAgIGJsb2NfMmggPSBjdXQoaGV1cmVfYXJyaXZlZSwgYnJlYWtzID0gYyg4LCAxMCwgMTIsIDE0LCAxNiwgMTgpLA0KICAgICAgICAgICAgICAgICAgcmlnaHQgPSBGQUxTRSwgaW5jbHVkZS5sb3dlc3QgPSBUUlVFKQ0KICApDQoNCmxhbWJkYV9wYXJfYmxvYyA8LSBkYXRhX3BhdGllbnQgJT4lDQogIGdyb3VwX2J5KGJsb2NfMmgpICU+JQ0KICBzdW1tYXJpc2UobmJfYXJyaXZlZXMgPSBuKCksIGxhbWJkYSA9IG5iX2Fycml2ZWVzIC8gMikNCg0KIyBQYXJhbcOodHJlcyBkZXMgbG9pcyBkZSBkdXLDqWUNCmR1cmVlIDwtIGRhdGFfcGF0aWVudCRkdXJlZV9zZWpvdXINCmZpdF9leHAgPC0gZml0ZGlzdHIoZHVyZWUsICJleHBvbmVudGlhbCIpDQpmaXRfZ2FtbWEgPC0gZml0ZGlzdHIoZHVyZWUsICJnYW1tYSIpDQpmaXRfd2VpYiA8LSBmaXRkaXN0cihkdXJlZSwgIndlaWJ1bGwiKQ0KDQojIEZvbmN0aW9uIHBvdXIgc2ltdWxlciB1bmUgam91cm7DqWUgY29tcGzDqHRlIChhcnJpdsOpZXMgKyBkdXLDqWVzKQ0Kc2ltdWxlcl9qb3VybmVlIDwtIGZ1bmN0aW9uKGxhbWJkYV9wYXJfYmxvYywgbG9pX2R1cmVlLCBwYXJhbXNfZHVyZWUpIHsNCiAgcGF0aWVudHMgPC0gZGF0YS5mcmFtZSgpDQogIA0KICBmb3IgKGkgaW4gMTpucm93KGxhbWJkYV9wYXJfYmxvYykpIHsNCiAgICBsYW1iZGEgPC0gbGFtYmRhX3Bhcl9ibG9jJGxhbWJkYVtpXQ0KICAgIGRlYnV0X2Jsb2MgPC0gYyg4LCAxMCwgMTIsIDE0LCAxNilbaV0NCiAgICBmaW5fYmxvYyA8LSBjKDEwLCAxMiwgMTQsIDE2LCAxOClbaV0NCiAgICANCiAgICAjIE5vbWJyZSBkJ2Fycml2w6llcyAoUG9pc3NvbikNCiAgICBuYl9hcnJpdmVlcyA8LSBycG9pcygxLCBsYW1iZGEgKiAyKQ0KICAgIA0KICAgIGlmIChuYl9hcnJpdmVlcyA+IDApIHsNCiAgICAgICMgVGVtcHMgZCdhcnJpdsOpZSB1bmlmb3JtZXMgZGFucyBsZSBibG9jDQogICAgICBhcnJpdmVlcyA8LSBydW5pZihuYl9hcnJpdmVlcywgbWluID0gZGVidXRfYmxvYywgbWF4ID0gZmluX2Jsb2MpDQogICAgICANCiAgICAgICMgRHVyw6llcyBzZWxvbiBsYSBsb2kgc3DDqWNpZmnDqWUNCiAgICAgIGlmIChsb2lfZHVyZWUgPT0gImV4cCIpIHsNCiAgICAgICAgZHVyZWVzIDwtIHJleHAobmJfYXJyaXZlZXMsIHJhdGUgPSAxL3BhcmFtc19kdXJlZSRtdSkNCiAgICAgIH0gZWxzZSBpZiAobG9pX2R1cmVlID09ICJnYW1tYSIpIHsNCiAgICAgICAgZHVyZWVzIDwtIHJnYW1tYShuYl9hcnJpdmVlcywgc2hhcGUgPSBwYXJhbXNfZHVyZWUkc2hhcGUsIHJhdGUgPSBwYXJhbXNfZHVyZWUkcmF0ZSkNCiAgICAgIH0gZWxzZSBpZiAobG9pX2R1cmVlID09ICJ3ZWlidWxsIikgew0KICAgICAgICBkdXJlZXMgPC0gcndlaWJ1bGwobmJfYXJyaXZlZXMsIHNoYXBlID0gcGFyYW1zX2R1cmVlJHNoYXBlLCBzY2FsZSA9IHBhcmFtc19kdXJlZSRzY2FsZSkNCiAgICAgIH0NCiAgICAgIA0KICAgICAgcGF0aWVudHMgPC0gcmJpbmQocGF0aWVudHMsIGRhdGEuZnJhbWUoDQogICAgICAgIGFycml2ZWUgPSBhcnJpdmVlcywgZHVyZWUgPSBkdXJlZXMsIGRlcGFydCA9IGFycml2ZWVzICsgZHVyZWVzDQogICAgICApKQ0KICAgIH0NCiAgfQ0KICByZXR1cm4ocGF0aWVudHMpDQp9DQoNCiMgRm9uY3Rpb24gcG91ciBjYWxjdWxlciBsJ29jY3VwYXRpb24gZCd1bmUgc2ltdWxhdGlvbg0KY2FsY3VsZXJfb2NjdXBhdGlvbl9zaW11bGF0aW9uIDwtIGZ1bmN0aW9uKHBhdGllbnRzX3NpbSwgZGVidXQgPSA4LCBmaW4gPSAxOCwgcGFzID0gMC4xKSB7DQogIHRlbXBzX3NlcSA8LSBzZXEoZGVidXQsIGZpbiwgYnkgPSBwYXMpDQogIG9jY3VwYXRpb24gPC0gc2FwcGx5KHRlbXBzX3NlcSwgZnVuY3Rpb24odCkgew0KICAgIHN1bShwYXRpZW50c19zaW0kYXJyaXZlZSA8PSB0ICYgcGF0aWVudHNfc2ltJGRlcGFydCA+PSB0KQ0KICB9KQ0KICBkYXRhLmZyYW1lKHRlbXBzID0gdGVtcHNfc2VxLCBvY2N1cGF0aW9uID0gb2NjdXBhdGlvbikNCn0NCg0KIyBMYW5jZXIgbGVzIDMwIHNpbXVsYXRpb25zIHBvdXIgY2hhcXVlIG1vZMOobGUNCnNldC5zZWVkKDEyMykNCm5fc2ltdWxhdGlvbnMgPC0gMzANCg0KcmVzdWx0YXRzX2V4cCA8LSBsYXBwbHkoMTpuX3NpbXVsYXRpb25zLCBmdW5jdGlvbihpKSB7DQogIGNhbGN1bGVyX29jY3VwYXRpb25fc2ltdWxhdGlvbigNCiAgICBzaW11bGVyX2pvdXJuZWUobGFtYmRhX3Bhcl9ibG9jLCAiZXhwIiwgbGlzdChtdSA9IDEvZml0X2V4cCRlc3RpbWF0ZVsicmF0ZSJdKSkNCiAgKQ0KfSkNCg0KcmVzdWx0YXRzX2dhbW1hIDwtIGxhcHBseSgxOm5fc2ltdWxhdGlvbnMsIGZ1bmN0aW9uKGkpIHsNCiAgY2FsY3VsZXJfb2NjdXBhdGlvbl9zaW11bGF0aW9uKA0KICAgIHNpbXVsZXJfam91cm5lZShsYW1iZGFfcGFyX2Jsb2MsICJnYW1tYSIsIA0KICAgICAgICAgICAgICAgICAgIGxpc3Qoc2hhcGUgPSBmaXRfZ2FtbWEkZXN0aW1hdGVbInNoYXBlIl0sIHJhdGUgPSBmaXRfZ2FtbWEkZXN0aW1hdGVbInJhdGUiXSkpDQogICkNCn0pDQoNCnJlc3VsdGF0c193ZWliIDwtIGxhcHBseSgxOm5fc2ltdWxhdGlvbnMsIGZ1bmN0aW9uKGkpIHsNCiAgY2FsY3VsZXJfb2NjdXBhdGlvbl9zaW11bGF0aW9uKA0KICAgIHNpbXVsZXJfam91cm5lZShsYW1iZGFfcGFyX2Jsb2MsICJ3ZWlidWxsIiwgDQogICAgICAgICAgICAgICAgICAgbGlzdChzaGFwZSA9IGZpdF93ZWliJGVzdGltYXRlWyJzaGFwZSJdLCBzY2FsZSA9IGZpdF93ZWliJGVzdGltYXRlWyJzY2FsZSJdKSkNCiAgKQ0KfSkNCg0KIyBBZ3LDqWdhdGlvbiBkZXMgcsOpc3VsdGF0cyAobW95ZW5uZSBldCBJQyA5MCUpDQphZ3JlZ2VyX3NpbXVsYXRpb25zIDwtIGZ1bmN0aW9uKGxpc3RlX3Jlc3VsdGF0cykgew0KICB0ZW1wcyA8LSBsaXN0ZV9yZXN1bHRhdHNbWzFdXSR0ZW1wcw0KICBtYXRfb2NjdXBhdGlvbiA8LSBzYXBwbHkobGlzdGVfcmVzdWx0YXRzLCBmdW5jdGlvbih4KSB4JG9jY3VwYXRpb24pDQogIGRhdGEuZnJhbWUoDQogICAgdGVtcHMgPSB0ZW1wcywNCiAgICBvY2N1cGF0aW9uX21veSA9IHJvd01lYW5zKG1hdF9vY2N1cGF0aW9uKSwNCiAgICBvY2N1cGF0aW9uX3EwNSA9IGFwcGx5KG1hdF9vY2N1cGF0aW9uLCAxLCBxdWFudGlsZSwgcHJvYnMgPSAwLjA1KSwNCiAgICBvY2N1cGF0aW9uX3E5NSA9IGFwcGx5KG1hdF9vY2N1cGF0aW9uLCAxLCBxdWFudGlsZSwgcHJvYnMgPSAwLjk1KQ0KICApDQp9DQoNCm9jY3VwYXRpb25fZXhwX2FnZyA8LSBhZ3JlZ2VyX3NpbXVsYXRpb25zKHJlc3VsdGF0c19leHApDQpvY2N1cGF0aW9uX2dhbW1hX2FnZyA8LSBhZ3JlZ2VyX3NpbXVsYXRpb25zKHJlc3VsdGF0c19nYW1tYSkNCm9jY3VwYXRpb25fd2VpYl9hZ2cgPC0gYWdyZWdlcl9zaW11bGF0aW9ucyhyZXN1bHRhdHNfd2VpYikNCg0Kb2NjdXBhdGlvbl9yZWVsbGVfcGxvdCA8LSBvY2N1cGF0aW9uX3JlZWxsZSAlPiUNCiAgbXV0YXRlKHRlbXBzX2hldXJlID0gYXMubnVtZXJpYyhkaWZmdGltZSh0ZW1wcywgZGVidXRfam91cm5lZSwgdW5pdHMgPSAiaG91cnMiKSkgKyA4KQ0KDQpnZ3Bsb3QoKSArDQogIGdlb21fbGluZShkYXRhID0gb2NjdXBhdGlvbl9yZWVsbGVfcGxvdCwgYWVzKHggPSB0ZW1wc19oZXVyZSwgeSA9IG9jY3VwYXRpb24pLCANCiAgICAgICAgICAgIGNvbG9yID0gImJsYWNrIiwgbGluZXdpZHRoID0gMS4yKSArDQogIGdlb21fbGluZShkYXRhID0gb2NjdXBhdGlvbl9leHBfYWdnLCBhZXMoeCA9IHRlbXBzLCB5ID0gb2NjdXBhdGlvbl9tb3kpLCANCiAgICAgICAgICAgIGNvbG9yID0gInJlZCIsIGxpbmV3aWR0aCA9IDAuOCwgbGluZXR5cGUgPSAiZGFzaGVkIikgKw0KICBnZW9tX3JpYmJvbihkYXRhID0gb2NjdXBhdGlvbl9leHBfYWdnLCBhZXMoeCA9IHRlbXBzLCB5bWluID0gb2NjdXBhdGlvbl9xMDUsIHltYXggPSBvY2N1cGF0aW9uX3E5NSksDQogICAgICAgICAgICAgIGZpbGwgPSAicmVkIiwgYWxwaGEgPSAwLjEpICsNCiAgZ2VvbV9saW5lKGRhdGEgPSBvY2N1cGF0aW9uX2dhbW1hX2FnZywgYWVzKHggPSB0ZW1wcywgeSA9IG9jY3VwYXRpb25fbW95KSwgDQogICAgICAgICAgICBjb2xvciA9ICJibHVlIiwgbGluZXdpZHRoID0gMC44LCBsaW5ldHlwZSA9ICJkYXNoZWQiKSArDQogIGdlb21fcmliYm9uKGRhdGEgPSBvY2N1cGF0aW9uX2dhbW1hX2FnZywgYWVzKHggPSB0ZW1wcywgeW1pbiA9IG9jY3VwYXRpb25fcTA1LCB5bWF4ID0gb2NjdXBhdGlvbl9xOTUpLA0KICAgICAgICAgICAgICBmaWxsID0gImJsdWUiLCBhbHBoYSA9IDAuMSkgKw0KICBnZW9tX2xpbmUoZGF0YSA9IG9jY3VwYXRpb25fd2VpYl9hZ2csIGFlcyh4ID0gdGVtcHMsIHkgPSBvY2N1cGF0aW9uX21veSksIA0KICAgICAgICAgICAgY29sb3IgPSAiZ3JlZW4iLCBsaW5ld2lkdGggPSAwLjgsIGxpbmV0eXBlID0gImRhc2hlZCIpICsNCiAgZ2VvbV9yaWJib24oZGF0YSA9IG9jY3VwYXRpb25fd2VpYl9hZ2csIGFlcyh4ID0gdGVtcHMsIHltaW4gPSBvY2N1cGF0aW9uX3EwNSwgeW1heCA9IG9jY3VwYXRpb25fcTk1KSwNCiAgICAgICAgICAgICAgZmlsbCA9ICJncmVlbiIsIGFscGhhID0gMC4xKSArDQogIGxhYnModGl0bGUgPSAiT2NjdXBhdGlvbiA6IFLDqWVsIHZzIE1vZMOobGVzIHNpbXVsw6lzICgzMCBqb3VybsOpZXMsIElDIDkwJSkiLA0KICAgICAgIHN1YnRpdGxlID0gIk5vaXIgPSBSw6llbCB8IFJvdWdlID0gRXhwIHwgQmxldSA9IEdhbW1hIHwgVmVydCA9IFdlaWJ1bGwiLA0KICAgICAgIHggPSAiSGV1cmUiLCB5ID0gIk5vbWJyZSBkZSBwYXRpZW50cyIpICsNCiAgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcyA9IHNlcSg4LCAxOCwgYnkgPSAyKSkgKw0KICB0aGVtZV9taW5pbWFsKCkNCg0KY2FsY3VsZXJfbWV0cmlxdWVzIDwtIGZ1bmN0aW9uKG9jY3VwYXRpb25fc2ltX2FnZywgb2NjdXBhdGlvbl9yZWVsbGVfcGxvdCkgew0KICBvY2N1cGF0aW9uX3JlZWxsZV9pbnRlcnAgPC0gYXBwcm94KA0KICAgIHggPSBvY2N1cGF0aW9uX3JlZWxsZV9wbG90JHRlbXBzX2hldXJlLA0KICAgIHkgPSBvY2N1cGF0aW9uX3JlZWxsZV9wbG90JG9jY3VwYXRpb24sDQogICAgeG91dCA9IG9jY3VwYXRpb25fc2ltX2FnZyR0ZW1wcw0KICApJHkNCiAgDQogIG1hZSA8LSBtZWFuKGFicyhvY2N1cGF0aW9uX3NpbV9hZ2ckb2NjdXBhdGlvbl9tb3kgLSBvY2N1cGF0aW9uX3JlZWxsZV9pbnRlcnApLCBuYS5ybSA9IFRSVUUpDQogIHJtc2UgPC0gc3FydChtZWFuKChvY2N1cGF0aW9uX3NpbV9hZ2ckb2NjdXBhdGlvbl9tb3kgLSBvY2N1cGF0aW9uX3JlZWxsZV9pbnRlcnApXjIsIG5hLnJtID0gVFJVRSkpDQogIGJpYWlzIDwtIG1lYW4ob2NjdXBhdGlvbl9zaW1fYWdnJG9jY3VwYXRpb25fbW95LCBuYS5ybSA9IFRSVUUpIC0gDQogICAgICAgICAgIG1lYW4ob2NjdXBhdGlvbl9yZWVsbGVfaW50ZXJwLCBuYS5ybSA9IFRSVUUpDQogIA0KICBjKE1BRSA9IG1hZSwgUk1TRSA9IHJtc2UsIEJpYWlzID0gYmlhaXMpDQp9DQoNCnRhYmxlYXVfbWV0cmlxdWVzIDwtIGRhdGEuZnJhbWUoDQogIE1vZMOobGUgPSBjKCJFeHBvbmVudGllbGxlIiwgIkdhbW1hIiwgIldlaWJ1bGwiKSwNCiAgTUFFID0gYyhjYWxjdWxlcl9tZXRyaXF1ZXMob2NjdXBhdGlvbl9leHBfYWdnLCBvY2N1cGF0aW9uX3JlZWxsZV9wbG90KVsiTUFFIl0sDQogICAgICAgICAgY2FsY3VsZXJfbWV0cmlxdWVzKG9jY3VwYXRpb25fZ2FtbWFfYWdnLCBvY2N1cGF0aW9uX3JlZWxsZV9wbG90KVsiTUFFIl0sDQogICAgICAgICAgY2FsY3VsZXJfbWV0cmlxdWVzKG9jY3VwYXRpb25fd2VpYl9hZ2csIG9jY3VwYXRpb25fcmVlbGxlX3Bsb3QpWyJNQUUiXSksDQogIFJNU0UgPSBjKGNhbGN1bGVyX21ldHJpcXVlcyhvY2N1cGF0aW9uX2V4cF9hZ2csIG9jY3VwYXRpb25fcmVlbGxlX3Bsb3QpWyJSTVNFIl0sDQogICAgICAgICAgIGNhbGN1bGVyX21ldHJpcXVlcyhvY2N1cGF0aW9uX2dhbW1hX2FnZywgb2NjdXBhdGlvbl9yZWVsbGVfcGxvdClbIlJNU0UiXSwNCiAgICAgICAgICAgY2FsY3VsZXJfbWV0cmlxdWVzKG9jY3VwYXRpb25fd2VpYl9hZ2csIG9jY3VwYXRpb25fcmVlbGxlX3Bsb3QpWyJSTVNFIl0pLA0KICBCaWFpcyA9IGMoY2FsY3VsZXJfbWV0cmlxdWVzKG9jY3VwYXRpb25fZXhwX2FnZywgb2NjdXBhdGlvbl9yZWVsbGVfcGxvdClbIkJpYWlzIl0sDQogICAgICAgICAgICBjYWxjdWxlcl9tZXRyaXF1ZXMob2NjdXBhdGlvbl9nYW1tYV9hZ2csIG9jY3VwYXRpb25fcmVlbGxlX3Bsb3QpWyJCaWFpcyJdLA0KICAgICAgICAgICAgY2FsY3VsZXJfbWV0cmlxdWVzKG9jY3VwYXRpb25fd2VpYl9hZ2csIG9jY3VwYXRpb25fcmVlbGxlX3Bsb3QpWyJCaWFpcyJdKQ0KKQ0KDQpwcmludCh0YWJsZWF1X21ldHJpcXVlcywgcm93Lm5hbWVzID0gRkFMU0UsIGRpZ2l0cyA9IDMpDQpgYGANCg0KRGFucyBjZXR0ZSBwcmVtacOocmUgcGFydGllLCB0cmVudGUgam91cm7DqWVzIGNvbXBsw6h0ZXMgb250IMOpdMOpIHNpbXVsw6llcw0KZW4gY29tYmluYW50IHVuIHByb2Nlc3N1cyBkZSBQb2lzc29uIHBhciBibG9jcyBob3JhaXJlcyBwb3VyIGxlcw0KYXJyaXbDqWVzIGV0IGRpZmbDqXJlbnRlcyBsb2lzIGRlIGR1csOpZSBkZSBzw6lqb3VyIChFeHBvbmVudGllbGxlLCBHYW1tYSBldA0KV2VpYnVsbCkuIExhIGNvbXBhcmFpc29uIGVudHJlIGzigJlvY2N1cGF0aW9uIHLDqWVsbGUgZXQgbGVzIHByw6lkaWN0aW9ucw0KbW95ZW5uZXMgbW9udHJlIHF1ZSBsZSBtb2TDqGxlIEdhbW1hIHJlcHJvZHVpdCBsZSBwbHVzIGZpZMOobGVtZW50IGxhDQpkeW5hbWlxdWUgb2JzZXJ2w6llLCBub3RhbW1lbnQgbGUgcGljIGTigJlvY2N1cGF0aW9uIGVuIG1pbGlldSBkZSBqb3VybsOpZS4NCkxlcyBpbnRlcnZhbGxlcyBkZSBjb25maWFuY2UgcmVzdGVudCB0b3V0ZWZvaXMgbGFyZ2VzLCB0cmFkdWlzYW50IHVuZQ0KZm9ydGUgdmFyaWFiaWxpdMOpIGludHJpbnPDqHF1ZSBkdSBzeXN0w6htZSwgZW4gcGFydGljdWxpZXIgZW4gZmluIGRlDQpqb3VybsOpZSBvw7kgZGVzIGVmZmV0cyBkZSBib3JkIGFwcGFyYWlzc2VudCBhdmVjIGRlcyBwYXRpZW50cyByZXN0YW50DQphcHLDqHMgbOKAmWhvcmFpcmUgZGUgZmVybWV0dXJlLg0KDQpMZXMgZG9ubsOpZXMgY29uZmlybWVudCBjZXMgb2JzZXJ2YXRpb25zIGdyYXBoaXF1ZXMuIExlIG1vZMOobGUgR2FtbWENCnByw6lzZW50ZSBs4oCZZXJyZXVyIG1veWVubmUgbGEgcGx1cyBmYWlibGUgZXQgdW4gYmlhaXMgcHJvY2hlIGRlIHrDqXJvLA0KdGFuZGlzIHF1ZSBsZSBtb2TDqGxlIGV4cG9uZW50aWVsIHN1cmVzdGltZSBs4oCZb2NjdXBhdGlvbiBldCBuZSBjYXB0dXJlDQpwYXMgY29ycmVjdGVtZW50IGxhIHZhcmlhYmlsaXTDqSBkZXMgZHVyw6llcyBkZSBzw6lqb3VyLiBDZXMgcsOpc3VsdGF0cyBzb250DQpjb2jDqXJlbnRzIGF2ZWMgbGVzIGFuYWx5c2VzIHByw6ljw6lkZW50ZXMgYmFzw6llcyBzdXIgbGVzIGNyaXTDqHJlcw0KZOKAmWluZm9ybWF0aW9uIGV0IGzigJnDqXR1ZGUgZHUgdGF1eCBkZSBkw6lmYWlsbGFuY2UsIGV0IHZhbGlkZW50IGxlIGNob2l4IGRlDQpsYSBsb2kgR2FtbWEgcG91ciBsYSBtb2TDqWxpc2F0aW9uIGRlcyBkdXLDqWVzLg0KDQpSZWZhaXRlcyBjZSB0cmF2YWlsIGQnZXN0aW1hdGlvbiBkZSBsJ29jY3VwYXRpb24gZW4gY29uc2lkw6lyYW50IGxlcw0KYXJyaXbDqWVzIGRlcyBwYXRpZW50cyBjb25udWVzIGV0IHZpc3VhbGlzZXIgZXQgYW5hbHlzZXIgbGVzIGdhaW5zIGVuDQp0ZXJtZSBkZSBxdWFsaXTDqSBkZSBwcsOpZGljdGlvbi4NCg0KKkVudHJleiB2b3RyZSB0ZXh0ZSBpY2kqDQoNCmBgYHtyfQ0KI19FbnRyZXogdm90cmUgY29kZSBSIGljaV8NCg0KIyBGb25jdGlvbiBkZSBzaW11bGF0aW9uIGF2ZWMgYXJyaXbDqWVzIGNvbm51ZXMNCnNpbXVsZXJfYXZlY19hcnJpdmVlc19jb25udWVzIDwtIGZ1bmN0aW9uKGRhdGFfcGF0aWVudCwgbG9pX2R1cmVlLCBwYXJhbXNfZHVyZWUsIG5fc2ltID0gMzApIHsNCiAgIyBFeHRyYWlyZSBsZXMgaGV1cmVzIGQnYXJyaXbDqWUgcsOpZWxsZXMNCiAgYXJyaXZlZXNfcmVlbGxlcyA8LSBob3VyKGRhdGFfcGF0aWVudCRhcnJpdmFsX3RpbWUpICsgbWludXRlKGRhdGFfcGF0aWVudCRhcnJpdmFsX3RpbWUpLzYwDQogIG5fcGF0aWVudHMgPC0gbnJvdyhkYXRhX3BhdGllbnQpDQogIA0KICAjIFNpbXVsZXIgbl9zaW0gam91cm7DqWVzDQogIGxhcHBseSgxOm5fc2ltLCBmdW5jdGlvbihpKSB7DQogICAgIyBHw6luw6lyZXIgbGVzIGR1csOpZXMgc2Vsb24gbGUgbW9kw6hsZQ0KICAgIGlmIChsb2lfZHVyZWUgPT0gImV4cCIpIHsNCiAgICAgIGR1cmVlcyA8LSByZXhwKG5fcGF0aWVudHMsIHJhdGUgPSAxL3BhcmFtc19kdXJlZSRtdSkNCiAgICB9IGVsc2UgaWYgKGxvaV9kdXJlZSA9PSAiZ2FtbWEiKSB7DQogICAgICBkdXJlZXMgPC0gcmdhbW1hKG5fcGF0aWVudHMsIHNoYXBlID0gcGFyYW1zX2R1cmVlJHNoYXBlLCByYXRlID0gcGFyYW1zX2R1cmVlJHJhdGUpDQogICAgfSBlbHNlIGlmIChsb2lfZHVyZWUgPT0gIndlaWJ1bGwiKSB7DQogICAgICBkdXJlZXMgPC0gcndlaWJ1bGwobl9wYXRpZW50cywgc2hhcGUgPSBwYXJhbXNfZHVyZWUkc2hhcGUsIHNjYWxlID0gcGFyYW1zX2R1cmVlJHNjYWxlKQ0KICAgIH0NCiAgICANCiAgICAjIENhbGN1bGVyIGwnb2NjdXBhdGlvbg0KICAgIGNhbGN1bGVyX29jY3VwYXRpb25fc2ltdWxhdGlvbigNCiAgICAgIGRhdGEuZnJhbWUoYXJyaXZlZSA9IGFycml2ZWVzX3JlZWxsZXMsIGR1cmVlID0gZHVyZWVzLCBkZXBhcnQgPSBhcnJpdmVlc19yZWVsbGVzICsgZHVyZWVzKQ0KICAgICkNCiAgfSkNCn0NCg0KIyBTaW11bGF0aW9ucyBwb3VyIGNoYXF1ZSBtb2TDqGxlDQpzZXQuc2VlZCg0NTYpDQpyZXN1bHRhdHNfZXhwX2Nvbm51ZXMgPC0gc2ltdWxlcl9hdmVjX2Fycml2ZWVzX2Nvbm51ZXMoDQogIGRhdGFfcGF0aWVudCwgImV4cCIsIGxpc3QobXUgPSAxL2ZpdF9leHAkZXN0aW1hdGVbInJhdGUiXSksIG5fc2ltdWxhdGlvbnMNCikNCg0KcmVzdWx0YXRzX2dhbW1hX2Nvbm51ZXMgPC0gc2ltdWxlcl9hdmVjX2Fycml2ZWVzX2Nvbm51ZXMoDQogIGRhdGFfcGF0aWVudCwgImdhbW1hIiwgDQogIGxpc3Qoc2hhcGUgPSBmaXRfZ2FtbWEkZXN0aW1hdGVbInNoYXBlIl0sIHJhdGUgPSBmaXRfZ2FtbWEkZXN0aW1hdGVbInJhdGUiXSksIA0KICBuX3NpbXVsYXRpb25zDQopDQoNCnJlc3VsdGF0c193ZWliX2Nvbm51ZXMgPC0gc2ltdWxlcl9hdmVjX2Fycml2ZWVzX2Nvbm51ZXMoDQogIGRhdGFfcGF0aWVudCwgIndlaWJ1bGwiLCANCiAgbGlzdChzaGFwZSA9IGZpdF93ZWliJGVzdGltYXRlWyJzaGFwZSJdLCBzY2FsZSA9IGZpdF93ZWliJGVzdGltYXRlWyJzY2FsZSJdKSwgDQogIG5fc2ltdWxhdGlvbnMNCikNCg0KIyBBZ3LDqWdhdGlvbg0Kb2NjdXBhdGlvbl9leHBfY29ubnVlc19hZ2cgPC0gYWdyZWdlcl9zaW11bGF0aW9ucyhyZXN1bHRhdHNfZXhwX2Nvbm51ZXMpDQpvY2N1cGF0aW9uX2dhbW1hX2Nvbm51ZXNfYWdnIDwtIGFncmVnZXJfc2ltdWxhdGlvbnMocmVzdWx0YXRzX2dhbW1hX2Nvbm51ZXMpDQpvY2N1cGF0aW9uX3dlaWJfY29ubnVlc19hZ2cgPC0gYWdyZWdlcl9zaW11bGF0aW9ucyhyZXN1bHRhdHNfd2VpYl9jb25udWVzKQ0KDQpnZ3Bsb3QoKSArDQogIGdlb21fbGluZShkYXRhID0gb2NjdXBhdGlvbl9yZWVsbGVfcGxvdCwgYWVzKHggPSB0ZW1wc19oZXVyZSwgeSA9IG9jY3VwYXRpb24pLCANCiAgICAgICAgICAgIGNvbG9yID0gImJsYWNrIiwgbGluZXdpZHRoID0gMS4yKSArDQogIGdlb21fbGluZShkYXRhID0gb2NjdXBhdGlvbl9leHBfY29ubnVlc19hZ2csIGFlcyh4ID0gdGVtcHMsIHkgPSBvY2N1cGF0aW9uX21veSksIA0KICAgICAgICAgICAgY29sb3IgPSAicmVkIiwgbGluZXdpZHRoID0gMC44LCBsaW5ldHlwZSA9ICJkYXNoZWQiKSArDQogIGdlb21fcmliYm9uKGRhdGEgPSBvY2N1cGF0aW9uX2V4cF9jb25udWVzX2FnZywgDQogICAgICAgICAgICAgIGFlcyh4ID0gdGVtcHMsIHltaW4gPSBvY2N1cGF0aW9uX3EwNSwgeW1heCA9IG9jY3VwYXRpb25fcTk1KSwNCiAgICAgICAgICAgICAgZmlsbCA9ICJyZWQiLCBhbHBoYSA9IDAuMTUpICsNCiAgZ2VvbV9saW5lKGRhdGEgPSBvY2N1cGF0aW9uX2dhbW1hX2Nvbm51ZXNfYWdnLCBhZXMoeCA9IHRlbXBzLCB5ID0gb2NjdXBhdGlvbl9tb3kpLCANCiAgICAgICAgICAgIGNvbG9yID0gImJsdWUiLCBsaW5ld2lkdGggPSAwLjgsIGxpbmV0eXBlID0gImRhc2hlZCIpICsNCiAgZ2VvbV9yaWJib24oZGF0YSA9IG9jY3VwYXRpb25fZ2FtbWFfY29ubnVlc19hZ2csIA0KICAgICAgICAgICAgICBhZXMoeCA9IHRlbXBzLCB5bWluID0gb2NjdXBhdGlvbl9xMDUsIHltYXggPSBvY2N1cGF0aW9uX3E5NSksDQogICAgICAgICAgICAgIGZpbGwgPSAiYmx1ZSIsIGFscGhhID0gMC4xNSkgKw0KICBnZW9tX2xpbmUoZGF0YSA9IG9jY3VwYXRpb25fd2VpYl9jb25udWVzX2FnZywgYWVzKHggPSB0ZW1wcywgeSA9IG9jY3VwYXRpb25fbW95KSwgDQogICAgICAgICAgICBjb2xvciA9ICJncmVlbiIsIGxpbmV3aWR0aCA9IDAuOCwgbGluZXR5cGUgPSAiZGFzaGVkIikgKw0KICBnZW9tX3JpYmJvbihkYXRhID0gb2NjdXBhdGlvbl93ZWliX2Nvbm51ZXNfYWdnLCANCiAgICAgICAgICAgICAgYWVzKHggPSB0ZW1wcywgeW1pbiA9IG9jY3VwYXRpb25fcTA1LCB5bWF4ID0gb2NjdXBhdGlvbl9xOTUpLA0KICAgICAgICAgICAgICBmaWxsID0gImdyZWVuIiwgYWxwaGEgPSAwLjE1KSArDQogIGxhYnModGl0bGUgPSAiT2NjdXBhdGlvbiBhdmVjIEFSUklWw4lFUyBDT05OVUVTICgzMCBzaW11bGF0aW9ucywgSUMgOTAlKSIsDQogICAgICAgc3VidGl0bGUgPSAiTm9pciA9IFLDqWVsIHwgUm91Z2UgPSBFeHAgfCBCbGV1ID0gR2FtbWEgfCBWZXJ0ID0gV2VpYnVsbCIsDQogICAgICAgeCA9ICJIZXVyZSIsIHkgPSAiTm9tYnJlIGRlIHBhdGllbnRzIikgKw0KICBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzID0gc2VxKDgsIDE4LCBieSA9IDIpKSArDQogIHRoZW1lX21pbmltYWwoKQ0KDQojIE3DqXRyaXF1ZXMgYXZlYyBhcnJpdsOpZXMgY29ubnVlcw0KbWV0cmlxdWVzX2V4cF9jb25udWVzIDwtIGNhbGN1bGVyX21ldHJpcXVlcyhvY2N1cGF0aW9uX2V4cF9jb25udWVzX2FnZywgb2NjdXBhdGlvbl9yZWVsbGVfcGxvdCkNCm1ldHJpcXVlc19nYW1tYV9jb25udWVzIDwtIGNhbGN1bGVyX21ldHJpcXVlcyhvY2N1cGF0aW9uX2dhbW1hX2Nvbm51ZXNfYWdnLCBvY2N1cGF0aW9uX3JlZWxsZV9wbG90KQ0KbWV0cmlxdWVzX3dlaWJfY29ubnVlcyA8LSBjYWxjdWxlcl9tZXRyaXF1ZXMob2NjdXBhdGlvbl93ZWliX2Nvbm51ZXNfYWdnLCBvY2N1cGF0aW9uX3JlZWxsZV9wbG90KQ0KDQojIFRhYmxlYXUgY29tcGFyYXRpZiBBVkFOVC9BUFLDiFMNCmNvbXBhcmFpc29uIDwtIGRhdGEuZnJhbWUoDQogIE1vZMOobGUgPSByZXAoYygiRXhwb25lbnRpZWxsZSIsICJHYW1tYSIsICJXZWlidWxsIiksIDIpLA0KICBBcHByb2NoZSA9IGMocmVwKCJBcnJpdsOpZXMgc2ltdWzDqWVzIiwgMyksIHJlcCgiQXJyaXbDqWVzIGNvbm51ZXMiLCAzKSksDQogIE1BRSA9IGMoDQogICAgY2FsY3VsZXJfbWV0cmlxdWVzKG9jY3VwYXRpb25fZXhwX2FnZywgb2NjdXBhdGlvbl9yZWVsbGVfcGxvdClbIk1BRSJdLA0KICAgIGNhbGN1bGVyX21ldHJpcXVlcyhvY2N1cGF0aW9uX2dhbW1hX2FnZywgb2NjdXBhdGlvbl9yZWVsbGVfcGxvdClbIk1BRSJdLA0KICAgIGNhbGN1bGVyX21ldHJpcXVlcyhvY2N1cGF0aW9uX3dlaWJfYWdnLCBvY2N1cGF0aW9uX3JlZWxsZV9wbG90KVsiTUFFIl0sDQogICAgbWV0cmlxdWVzX2V4cF9jb25udWVzWyJNQUUiXSwNCiAgICBtZXRyaXF1ZXNfZ2FtbWFfY29ubnVlc1siTUFFIl0sDQogICAgbWV0cmlxdWVzX3dlaWJfY29ubnVlc1siTUFFIl0NCiAgKSwNCiAgUk1TRSA9IGMoDQogICAgY2FsY3VsZXJfbWV0cmlxdWVzKG9jY3VwYXRpb25fZXhwX2FnZywgb2NjdXBhdGlvbl9yZWVsbGVfcGxvdClbIlJNU0UiXSwNCiAgICBjYWxjdWxlcl9tZXRyaXF1ZXMob2NjdXBhdGlvbl9nYW1tYV9hZ2csIG9jY3VwYXRpb25fcmVlbGxlX3Bsb3QpWyJSTVNFIl0sDQogICAgY2FsY3VsZXJfbWV0cmlxdWVzKG9jY3VwYXRpb25fd2VpYl9hZ2csIG9jY3VwYXRpb25fcmVlbGxlX3Bsb3QpWyJSTVNFIl0sDQogICAgbWV0cmlxdWVzX2V4cF9jb25udWVzWyJSTVNFIl0sDQogICAgbWV0cmlxdWVzX2dhbW1hX2Nvbm51ZXNbIlJNU0UiXSwNCiAgICBtZXRyaXF1ZXNfd2VpYl9jb25udWVzWyJSTVNFIl0NCiAgKQ0KKQ0KDQpwcmludChjb21wYXJhaXNvbiwgcm93Lm5hbWVzID0gRkFMU0UsIGRpZ2l0cyA9IDMpDQoNCiMgQ2FsY3VsIGRlcyBnYWlucw0KZ2FpbnMgPC0gZGF0YS5mcmFtZSgNCiAgTW9kw6hsZSA9IGMoIkV4cG9uZW50aWVsbGUiLCAiR2FtbWEiLCAiV2VpYnVsbCIpLA0KICBSZWR1Y3Rpb25fTUFFX3BjdCA9IGMoDQogICAgKGNhbGN1bGVyX21ldHJpcXVlcyhvY2N1cGF0aW9uX2V4cF9hZ2csIG9jY3VwYXRpb25fcmVlbGxlX3Bsb3QpWyJNQUUiXSAtIA0KICAgICBtZXRyaXF1ZXNfZXhwX2Nvbm51ZXNbIk1BRSJdKSAvIA0KICAgICBjYWxjdWxlcl9tZXRyaXF1ZXMob2NjdXBhdGlvbl9leHBfYWdnLCBvY2N1cGF0aW9uX3JlZWxsZV9wbG90KVsiTUFFIl0gKiAxMDAsDQogICAgDQogICAgKGNhbGN1bGVyX21ldHJpcXVlcyhvY2N1cGF0aW9uX2dhbW1hX2FnZywgb2NjdXBhdGlvbl9yZWVsbGVfcGxvdClbIk1BRSJdIC0gDQogICAgIG1ldHJpcXVlc19nYW1tYV9jb25udWVzWyJNQUUiXSkgLyANCiAgICAgY2FsY3VsZXJfbWV0cmlxdWVzKG9jY3VwYXRpb25fZ2FtbWFfYWdnLCBvY2N1cGF0aW9uX3JlZWxsZV9wbG90KVsiTUFFIl0gKiAxMDAsDQogICAgDQogICAgKGNhbGN1bGVyX21ldHJpcXVlcyhvY2N1cGF0aW9uX3dlaWJfYWdnLCBvY2N1cGF0aW9uX3JlZWxsZV9wbG90KVsiTUFFIl0gLSANCiAgICAgbWV0cmlxdWVzX3dlaWJfY29ubnVlc1siTUFFIl0pIC8gDQogICAgIGNhbGN1bGVyX21ldHJpcXVlcyhvY2N1cGF0aW9uX3dlaWJfYWdnLCBvY2N1cGF0aW9uX3JlZWxsZV9wbG90KVsiTUFFIl0gKiAxMDANCiAgKSwNCiAgUmVkdWN0aW9uX1JNU0VfcGN0ID0gYygNCiAgICAoY2FsY3VsZXJfbWV0cmlxdWVzKG9jY3VwYXRpb25fZXhwX2FnZywgb2NjdXBhdGlvbl9yZWVsbGVfcGxvdClbIlJNU0UiXSAtIA0KICAgICBtZXRyaXF1ZXNfZXhwX2Nvbm51ZXNbIlJNU0UiXSkgLyANCiAgICAgY2FsY3VsZXJfbWV0cmlxdWVzKG9jY3VwYXRpb25fZXhwX2FnZywgb2NjdXBhdGlvbl9yZWVsbGVfcGxvdClbIlJNU0UiXSAqIDEwMCwNCiAgICANCiAgICAoY2FsY3VsZXJfbWV0cmlxdWVzKG9jY3VwYXRpb25fZ2FtbWFfYWdnLCBvY2N1cGF0aW9uX3JlZWxsZV9wbG90KVsiUk1TRSJdIC0gDQogICAgIG1ldHJpcXVlc19nYW1tYV9jb25udWVzWyJSTVNFIl0pIC8gDQogICAgIGNhbGN1bGVyX21ldHJpcXVlcyhvY2N1cGF0aW9uX2dhbW1hX2FnZywgb2NjdXBhdGlvbl9yZWVsbGVfcGxvdClbIlJNU0UiXSAqIDEwMCwNCiAgICANCiAgICAoY2FsY3VsZXJfbWV0cmlxdWVzKG9jY3VwYXRpb25fd2VpYl9hZ2csIG9jY3VwYXRpb25fcmVlbGxlX3Bsb3QpWyJSTVNFIl0gLSANCiAgICAgbWV0cmlxdWVzX3dlaWJfY29ubnVlc1siUk1TRSJdKSAvIA0KICAgICBjYWxjdWxlcl9tZXRyaXF1ZXMob2NjdXBhdGlvbl93ZWliX2FnZywgb2NjdXBhdGlvbl9yZWVsbGVfcGxvdClbIlJNU0UiXSAqIDEwMA0KICApDQopDQoNCmNhdCgiXG49PT0gR0FJTlMgRU4gUVVBTElUw4kgREUgUFLDiURJQ1RJT04gPT09XG4iKQ0KcHJpbnQoZ2FpbnMsIHJvdy5uYW1lcyA9IEZBTFNFLCBkaWdpdHMgPSAxKQ0KYGBgDQoNCmBgYCAgICAgICAgIA0KTm90ZXMgOiBJY2ksIG9uIG5lIHPDqXBhcmUgcGFzIGxlcyBkb25uw6llcyBlbiBkb25uw6llcyBkJ2VudHJhw65uZW1lbnQgZXQgZGUgdGVzdCBwb3VyIGRlcyByYWlzb25zIHByYXRpcXVlcyBkZSBxdWFudGl0w6kgZGUgZG9ubsOpZXMgbWFpcyBjJ2VzdCB1biBwb2ludCDDoCB0ZW5pciBlbiBjb21wdGUgZGFucyB1bmUgdnJhaSB2YWxpZGF0aW9uIGRlIG1vZMOobGUuDQpgYGANCg0KRGFucyBjZXR0ZSBzZWNvbmRlIHBhcnRpZSwgbGVzIGhldXJlcyBk4oCZYXJyaXbDqWUgcsOpZWxsZXMgb250IMOpdMOpDQp1dGlsaXPDqWVzIHRvdXQgZW4gY29uc2VydmFudCBsYSBzaW11bGF0aW9uIGRlcyBkdXLDqWVzIGRlIHPDqWpvdXIuIENldHRlDQphcHByb2NoZSBwZXJtZXQgZOKAmWlzb2xlciBs4oCZaW1wYWN0IGR1IG1vZMOobGUgZGUgZHVyw6llIGluZMOpcGVuZGFtbWVudCBkZQ0KbOKAmWluY2VydGl0dWRlIGxpw6llIGF1eCBhcnJpdsOpZXMuIExlcyBwZXJmb3JtYW5jZXMgc+KAmWFtw6lsaW9yZW50IGFsb3JzDQpuZXR0ZW1lbnQsIGF2ZWMgdW5lIHLDqWR1Y3Rpb24gZGUgbOKAmWVycmV1ciBtb3llbm5lIGRlIGzigJlvcmRyZSBkZSAzMCDDoCA1MA0KcG91ciBjZW50IHNlbG9uIGxlcyBtb2TDqGxlcywgZXQgZGVzIGludGVydmFsbGVzIGRlIGNvbmZpYW5jZSBiZWF1Y291cA0KcGx1cyDDqXRyb2l0cy4gQ2VsYSBtb250cmUgcXVlIGxhIHZhcmlhYmlsaXTDqSBkdSBwcm9jZXNzdXMgZOKAmWFycml2w6llDQpjb25zdGl0dWUgbGEgcHJpbmNpcGFsZSBzb3VyY2UgZOKAmWluY2VydGl0dWRlIGRhbnMgbGEgcHLDqWRpY3Rpb24gZGUNCmzigJlvY2N1cGF0aW9uLg0KDQpE4oCZdW4gcG9pbnQgZGUgdnVlIG9ww6lyYXRpb25uZWwsIGNlcyByw6lzdWx0YXRzIHNvdWxpZ25lbnQgbOKAmWludMOpcsOqdA0KbWFqZXVyIGTigJl1biBzdWl2aSBlbiB0ZW1wcyByw6llbCBkZXMgYXJyaXbDqWVzLiBVbmUgbWVpbGxldXJlIGNvbm5haXNzYW5jZQ0KZGVzIGZsdXggZW50cmFudHMgcGVybWV0dHJhaXQgZOKAmWFudGljaXBlciBsZXMgcGljcyBk4oCZb2NjdXBhdGlvbiwNCmTigJlhZGFwdGVyIGxlcyByZXNzb3VyY2VzIGF1IGNvdXJzIGRlIGxhIGpvdXJuw6llIGV0IGRlIGxpbWl0ZXIgbGVzDQpwaMOpbm9tw6huZXMgZGUgY29uZ2VzdGlvbi4gRW4gY29uY2x1c2lvbiwgbGUgbW9kw6hsZSBjb21iaW5hbnQgdW4NCnByb2Nlc3N1cyBk4oCZYXJyaXbDqWUgZGUgUG9pc3NvbiBwYXIgYmxvY3MgaG9yYWlyZXMgZXQgdW5lIGxvaSBHYW1tYSBwb3VyDQpsZXMgZHVyw6llcyBkZSBzw6lqb3VyIG9mZnJlIHVuIGNvbXByb21pcyBwZXJ0aW5lbnQgZW50cmUgc2ltcGxpY2l0w6kgZXQNCnByw6ljaXNpb24sIGV0IGNvbnN0aXR1ZSB1bmUgYmFzZSBzb2xpZGUgcG91ciB1bmUgZ2VzdGlvbiBkeW5hbWlxdWUgZXQNCm9wdGltaXPDqWUgZHUgc2VydmljZSBk4oCZdXJvbG9naWUuDQoNCiMjIyBRdWVzdGlvbiBib251cyAoLzEpIDoNCg0KUXVlbGxlcyBzb250IGxlcyBub3RhdGlvbnMgZGUgS2VuZGFsbCBkZXMgZGlmZsOpcmVudHMgbW9kw6hsZXMgZGUgImZpbGUNCmQnYXR0ZW50ZSIgcXVlIHZvdXMgYXZleiBpbXBsw6ltZW50w6lzID8gKEp1c3RpZmlleikNCg0KYGBgICAgICAgICAgDQpOb3RlIDogIE9uIGVzdCBpY2kgc3VyIHVuIGNhcyBzcMOpY2lhbCBsZSBtb2TDqGxlIGRlIGZpbGUgZCdhdHRlbnRlIGVzdCBzYW5zIGZpbGUgZCdhdHRlbnRlDQpgYGANCg0KTGEgbm90YXRpb24gZGUgS2VuZGFsbCBwb3VyIGxlcyBmaWxlcyBkJ2F0dGVudGUgZXN0IDogQS9CL2MvSy9OL0QNCg0KT8O5IDogLSBBID0gcHJvY2Vzc3VzIGQnYXJyaXbDqWUgLSBCID0gcHJvY2Vzc3VzIGRlIHNlcnZpY2UgLSBjID0gbm9tYnJlDQpkZSBzZXJ2ZXVycyAtIEsgPSBjYXBhY2l0w6kgZHUgc3lzdMOobWUgLSBOID0gcG9wdWxhdGlvbiBzb3VyY2UgLSBEID0NCmRpc2NpcGxpbmUgZGUgbGEgZmlsZQ0KDQpQb3VyIG5vcyBtb2TDqGxlcyA6DQoNCkNvbW1lIGluZGlxdcOpLCBvbiBlc3QgZGFucyB1biBjYXMgc3DDqWNpYWwgc2FucyBmaWxlIGQnYXR0ZW50ZSAobGVzDQpwYXRpZW50cyBzb250IGRpcmVjdGVtZW50IHByaXMgZW4gY2hhcmdlIMOgIGwnYXJyaXbDqWUsIHBhcyBkJ2F0dGVudGUgcG91cg0Kw6p0cmUgc2VydmkpLg0KDQpNb2TDqGxlIGF2ZWMgYXJyaXbDqWVzIHNpbXVsw6llcyA6IC0gQXJyaXbDqWVzIDogUG9pc3NvbiBub24taG9tb2fDqG5lICh0YXV4DQp2YXJpYWJsZSBwYXIgYmxvYyBkZSAyaCkg4oaSIE10L0cv4oieIC0gU2VydmljZSA6IEdhbW1hLCBXZWlidWxsLCBvdQ0KRXhwb25lbnRpZWxsZSBzZWxvbiBsZSBtb2TDqGxlIC0gU2VydmV1cnMgOiDiiJ4gKG91IHN1ZmZpc2FtbWVudCBub21icmV1eA0KcG91ciDDqXZpdGVyIGwnYXR0ZW50ZSkgLSBQYXMgZGUgbGltaXRlIGRlIGNhcGFjaXTDqSwgcG9wdWxhdGlvbiBpbmZpbmllDQoNCk5vdGF0aW9uIDogTXQvRy/iiJ4gb3UgTXQvR2FtbWEv4oieLCBNdC9XZWlidWxsL+KIniwgTXQvRXhwL+KIng0KDQpMZSBNdCBpbmRpcXVlIHVuIHByb2Nlc3N1cyBkZSBQb2lzc29uIG5vbi1ob21vZ8OobmUgKHRhdXggZMOpcGVuZCBkdSB0ZW1wcw0KdCkuIExlIEcgKEdlbmVyYWwpIGluZGlxdWUgdW5lIGRpc3RyaWJ1dGlvbiBnw6luw6lyYWxlIGRlIHNlcnZpY2UuIExlIOKIng0KaW5kaXF1ZSB1biBub21icmUgaWxsaW1pdMOpIGRlIHNlcnZldXJzIChwYXMgZCdhdHRlbnRlKS4NCg0KTW9kw6hsZSBhdmVjIGFycml2w6llcyBjb25udWVzIDogLSBBcnJpdsOpZXMgOiBEw6l0ZXJtaW5pc3RlcyAoY29ubnVlcyDDoA0KbCdhdmFuY2UpIOKGkiBEL0cv4oieIC0gU2VydmljZSA6IGlkZW0gY2ktZGVzc3VzIC0gU2VydmV1cnMgOiDiiJ4NCg0KTm90YXRpb24gOiBEL0dhbW1hL+KIniwgRC9XZWlidWxsL+KIniwgRC9FeHAv4oieDQoNCkxlIEQgKERldGVybWluaXN0aWMpIGluZGlxdWUgcXVlIGxlcyBhcnJpdsOpZXMgc29udCBkw6l0ZXJtaW5pc3RlcyBldA0KY29ubnVlcy4NCg0KSnVzdGlmaWNhdGlvbiA6IC0gUGFzIGRlIGZpbGUgZCdhdHRlbnRlIGNhciBjaGFxdWUgcGF0aWVudCBlc3QgcHJpcyBlbg0KY2hhcmdlIGltbcOpZGlhdGVtZW50IC0gTGUgc3lzdMOobWUgYSB1bmUgY2FwYWNpdMOpIHN1ZmZpc2FudGUgKG3DqWRlY2lucywNCnNhbGxlcykgcG91ciDDqXZpdGVyIGwnYXR0ZW50ZSAtIEMnZXN0IGNvaMOpcmVudCBhdmVjIHVuIHNlcnZpY2UgZGUNCmNvbnN1bHRhdGlvbnMgZXh0ZXJuZXMgb8O5IGxlcyBSRFYgc29udCBwbGFuaWZpw6lzDQo=